From 247bc5ab7245b98f32b844c29e7051a7f492f198 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Thu, 25 Nov 2021 23:54:03 +0100 Subject: [PATCH 01/56] Add to zip without loading into memory --- Files.sln | 4 +- ICSharpCode.SharpZipLib/AssemblyInfo.cs | 3 + ICSharpCode.SharpZipLib/BZip2/BZip2.cs | 79 + .../BZip2/BZip2Constants.cs | 117 + .../BZip2/BZip2Exception.cs | 54 + .../BZip2/BZip2InputStream.cs | 1053 ++++ .../BZip2/BZip2OutputStream.cs | 2033 +++++++ ICSharpCode.SharpZipLib/Checksum/Adler32.cs | 163 + ICSharpCode.SharpZipLib/Checksum/BZip2Crc.cs | 171 + ICSharpCode.SharpZipLib/Checksum/Crc32.cs | 173 + .../Checksum/CrcUtilities.cs | 158 + ICSharpCode.SharpZipLib/Checksum/IChecksum.cs | 51 + .../Core/ByteOrderUtils.cs | 130 + ICSharpCode.SharpZipLib/Core/EmptyRefs.cs | 17 + .../Core/Exceptions/SharpZipBaseException.cs | 58 + .../Exceptions/StreamDecodingException.cs | 50 + .../Exceptions/StreamUnsupportedException.cs | 49 + .../UnexpectedEndOfStreamException.cs | 49 + .../Exceptions/ValueOutOfRangeException.cs | 66 + .../Core/FileSystemScanner.cs | 545 ++ .../Core/INameTransform.cs | 22 + ICSharpCode.SharpZipLib/Core/IScanFilter.cs | 15 + .../Core/InvalidNameException.cs | 53 + ICSharpCode.SharpZipLib/Core/NameFilter.cs | 284 + ICSharpCode.SharpZipLib/Core/PathFilter.cs | 318 ++ ICSharpCode.SharpZipLib/Core/PathUtils.cs | 54 + ICSharpCode.SharpZipLib/Core/StreamUtils.cs | 295 + .../Encryption/PkzipClassic.cs | 487 ++ .../Encryption/ZipAESStream.cs | 230 + .../Encryption/ZipAESTransform.cs | 224 + ICSharpCode.SharpZipLib/GZip/GZip.cs | 92 + ICSharpCode.SharpZipLib/GZip/GZipConstants.cs | 78 + ICSharpCode.SharpZipLib/GZip/GZipException.cs | 54 + .../GZip/GzipInputStream.cs | 361 ++ .../GZip/GzipOutputStream.cs | 293 + .../ICSharpCode.SharpZipLib.csproj | 44 + ICSharpCode.SharpZipLib/Lzw/LzwConstants.cs | 63 + ICSharpCode.SharpZipLib/Lzw/LzwException.cs | 54 + ICSharpCode.SharpZipLib/Lzw/LzwInputStream.cs | 572 ++ .../Tar/InvalidHeaderException.cs | 55 + ICSharpCode.SharpZipLib/Tar/TarArchive.cs | 1028 ++++ ICSharpCode.SharpZipLib/Tar/TarBuffer.cs | 599 ++ ICSharpCode.SharpZipLib/Tar/TarEntry.cs | 598 ++ ICSharpCode.SharpZipLib/Tar/TarException.cs | 54 + .../Tar/TarExtendedHeaderReader.cs | 99 + ICSharpCode.SharpZipLib/Tar/TarHeader.cs | 1310 +++++ ICSharpCode.SharpZipLib/Tar/TarInputStream.cs | 771 +++ .../Tar/TarOutputStream.cs | 522 ++ .../Zip/Compression/Deflater.cs | 604 ++ .../Zip/Compression/DeflaterConstants.cs | 146 + .../Zip/Compression/DeflaterEngine.cs | 946 +++ .../Zip/Compression/DeflaterHuffman.cs | 959 ++++ .../Zip/Compression/DeflaterPending.cs | 17 + .../Zip/Compression/Inflater.cs | 887 +++ .../Zip/Compression/InflaterDynHeader.cs | 151 + .../Zip/Compression/InflaterHuffmanTree.cs | 237 + .../Zip/Compression/PendingBuffer.cs | 268 + .../Streams/DeflaterOutputStream.cs | 513 ++ .../Streams/InflaterInputStream.cs | 713 +++ .../Zip/Compression/Streams/OutputWindow.cs | 220 + .../Compression/Streams/StreamManipulator.cs | 298 + ICSharpCode.SharpZipLib/Zip/FastZip.cs | 1003 ++++ ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs | 67 + .../Zip/WindowsNameTransform.cs | 266 + ICSharpCode.SharpZipLib/Zip/ZipConstants.cs | 475 ++ .../Zip/ZipEncryptionMethod.cs | 28 + ICSharpCode.SharpZipLib/Zip/ZipEntry.cs | 1157 ++++ .../Zip/ZipEntryExtensions.cs | 32 + .../Zip/ZipEntryFactory.cs | 375 ++ ICSharpCode.SharpZipLib/Zip/ZipException.cs | 54 + ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs | 974 ++++ ICSharpCode.SharpZipLib/Zip/ZipFile.cs | 5059 +++++++++++++++++ ICSharpCode.SharpZipLib/Zip/ZipFormat.cs | 597 ++ .../Zip/ZipHelperStream.cs | 0 ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs | 730 +++ .../Zip/ZipNameTransform.cs | 313 + .../Zip/ZipOutputStream.cs | 1004 ++++ ICSharpCode.SharpZipLib/Zip/ZipStrings.cs | 213 + src/Files/Files.csproj | 9 +- src/Files/MultilingualResources/Files.af.xlf | 68 +- src/Files/MultilingualResources/Files.ar.xlf | 56 +- src/Files/MultilingualResources/Files.ca.xlf | 56 +- .../MultilingualResources/Files.cs-CZ.xlf | 56 +- .../MultilingualResources/Files.da-DK.xlf | 56 +- src/Files/MultilingualResources/Files.da.xlf | 56 +- .../MultilingualResources/Files.de-DE.xlf | 58 +- src/Files/MultilingualResources/Files.el.xlf | 56 +- .../MultilingualResources/Files.en-GB.xlf | 56 +- .../MultilingualResources/Files.es-419.xlf | 56 +- .../MultilingualResources/Files.es-ES.xlf | 56 +- .../MultilingualResources/Files.fr-FR.xlf | 56 +- .../MultilingualResources/Files.he-IL.xlf | 56 +- .../MultilingualResources/Files.hi-IN.xlf | 56 +- .../MultilingualResources/Files.hr-HR.xlf | 56 +- .../MultilingualResources/Files.hu-HU.xlf | 56 +- .../MultilingualResources/Files.id-ID.xlf | 56 +- .../MultilingualResources/Files.it-IT.xlf | 56 +- .../MultilingualResources/Files.ja-JP.xlf | 56 +- src/Files/MultilingualResources/Files.ka.xlf | 56 +- .../MultilingualResources/Files.ko-KR.xlf | 56 +- .../MultilingualResources/Files.lv-LV.xlf | 56 +- .../MultilingualResources/Files.nl-NL.xlf | 56 +- .../MultilingualResources/Files.or-IN.xlf | 56 +- .../MultilingualResources/Files.pl-PL.xlf | 56 +- .../MultilingualResources/Files.pt-BR.xlf | 56 +- .../MultilingualResources/Files.pt-PT.xlf | 58 +- .../MultilingualResources/Files.ru-RU.xlf | 56 +- .../MultilingualResources/Files.sv-SE.xlf | 56 +- src/Files/MultilingualResources/Files.ta.xlf | 56 +- .../MultilingualResources/Files.tr-TR.xlf | 56 +- .../MultilingualResources/Files.uk-UA.xlf | 56 +- src/Files/MultilingualResources/Files.vi.xlf | 56 +- .../MultilingualResources/Files.zh-Hans.xlf | 56 +- .../MultilingualResources/Files.zh-Hant.xlf | 56 +- src/Files/Strings/af/Resources.resw | 2727 +++++++++ src/Files/Strings/de-DE/Resources.resw | 88 +- src/Files/Strings/el/Resources.resw | 243 +- src/Files/Strings/en-GB/Resources.resw | 50 +- src/Files/Strings/es-419/Resources.resw | 18 - src/Files/Strings/es-ES/Resources.resw | 18 - src/Files/Strings/fr-FR/Resources.resw | 18 - src/Files/Strings/hr-HR/Resources.resw | 18 - src/Files/Strings/hu-HU/Resources.resw | 18 - src/Files/Strings/id-ID/Resources.resw | 18 - src/Files/Strings/it-IT/Resources.resw | 18 - src/Files/Strings/ja-JP/Resources.resw | 18 - src/Files/Strings/ka/Resources.resw | 18 - src/Files/Strings/ko-KR/Resources.resw | 74 +- src/Files/Strings/lv-LV/Resources.resw | 18 - src/Files/Strings/pl-PL/Resources.resw | 18 - src/Files/Strings/pt-BR/Resources.resw | 18 - src/Files/Strings/pt-PT/Resources.resw | 52 +- src/Files/Strings/ru-RU/Resources.resw | 18 - src/Files/Strings/zh-Hans/Resources.resw | 48 +- src/Files/Strings/zh-Hant/Resources.resw | 18 - 135 files changed, 36130 insertions(+), 1347 deletions(-) create mode 100644 ICSharpCode.SharpZipLib/AssemblyInfo.cs create mode 100644 ICSharpCode.SharpZipLib/BZip2/BZip2.cs create mode 100644 ICSharpCode.SharpZipLib/BZip2/BZip2Constants.cs create mode 100644 ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs create mode 100644 ICSharpCode.SharpZipLib/BZip2/BZip2InputStream.cs create mode 100644 ICSharpCode.SharpZipLib/BZip2/BZip2OutputStream.cs create mode 100644 ICSharpCode.SharpZipLib/Checksum/Adler32.cs create mode 100644 ICSharpCode.SharpZipLib/Checksum/BZip2Crc.cs create mode 100644 ICSharpCode.SharpZipLib/Checksum/Crc32.cs create mode 100644 ICSharpCode.SharpZipLib/Checksum/CrcUtilities.cs create mode 100644 ICSharpCode.SharpZipLib/Checksum/IChecksum.cs create mode 100644 ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs create mode 100644 ICSharpCode.SharpZipLib/Core/EmptyRefs.cs create mode 100644 ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs create mode 100644 ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs create mode 100644 ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs create mode 100644 ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs create mode 100644 ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs create mode 100644 ICSharpCode.SharpZipLib/Core/FileSystemScanner.cs create mode 100644 ICSharpCode.SharpZipLib/Core/INameTransform.cs create mode 100644 ICSharpCode.SharpZipLib/Core/IScanFilter.cs create mode 100644 ICSharpCode.SharpZipLib/Core/InvalidNameException.cs create mode 100644 ICSharpCode.SharpZipLib/Core/NameFilter.cs create mode 100644 ICSharpCode.SharpZipLib/Core/PathFilter.cs create mode 100644 ICSharpCode.SharpZipLib/Core/PathUtils.cs create mode 100644 ICSharpCode.SharpZipLib/Core/StreamUtils.cs create mode 100644 ICSharpCode.SharpZipLib/Encryption/PkzipClassic.cs create mode 100644 ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs create mode 100644 ICSharpCode.SharpZipLib/Encryption/ZipAESTransform.cs create mode 100644 ICSharpCode.SharpZipLib/GZip/GZip.cs create mode 100644 ICSharpCode.SharpZipLib/GZip/GZipConstants.cs create mode 100644 ICSharpCode.SharpZipLib/GZip/GZipException.cs create mode 100644 ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs create mode 100644 ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs create mode 100644 ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj create mode 100644 ICSharpCode.SharpZipLib/Lzw/LzwConstants.cs create mode 100644 ICSharpCode.SharpZipLib/Lzw/LzwException.cs create mode 100644 ICSharpCode.SharpZipLib/Lzw/LzwInputStream.cs create mode 100644 ICSharpCode.SharpZipLib/Tar/InvalidHeaderException.cs create mode 100644 ICSharpCode.SharpZipLib/Tar/TarArchive.cs create mode 100644 ICSharpCode.SharpZipLib/Tar/TarBuffer.cs create mode 100644 ICSharpCode.SharpZipLib/Tar/TarEntry.cs create mode 100644 ICSharpCode.SharpZipLib/Tar/TarException.cs create mode 100644 ICSharpCode.SharpZipLib/Tar/TarExtendedHeaderReader.cs create mode 100644 ICSharpCode.SharpZipLib/Tar/TarHeader.cs create mode 100644 ICSharpCode.SharpZipLib/Tar/TarInputStream.cs create mode 100644 ICSharpCode.SharpZipLib/Tar/TarOutputStream.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Deflater.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/DeflaterConstants.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/DeflaterHuffman.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/DeflaterPending.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Inflater.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/InflaterDynHeader.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/InflaterHuffmanTree.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/PendingBuffer.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Streams/OutputWindow.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Streams/StreamManipulator.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/FastZip.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/WindowsNameTransform.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipConstants.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipEncryptionMethod.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipEntry.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipEntryExtensions.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipException.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipFile.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipFormat.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipNameTransform.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs create mode 100644 ICSharpCode.SharpZipLib/Zip/ZipStrings.cs diff --git a/Files.sln b/Files.sln index 5c66a1834839..b9fb7d1b67ab 100644 --- a/Files.sln +++ b/Files.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.1.31903.286 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31911.196 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.Launcher", "src\Files.Launcher\Files.Launcher.csproj", "{533F9E86-EE0A-4FCB-B70C-F29532C1B787}" ProjectSection(ProjectDependencies) = postProject diff --git a/ICSharpCode.SharpZipLib/AssemblyInfo.cs b/ICSharpCode.SharpZipLib/AssemblyInfo.cs new file mode 100644 index 000000000000..8f8e620164ac --- /dev/null +++ b/ICSharpCode.SharpZipLib/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("ICSharpCode.SharpZipLib.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b9a14ea8fc9d7599e0e82a1292a23103f0210e2f928a0f466963af23fffadba59dcc8c9e26ecd114d7c0b4179e4bc93b1656b7ee2d4a67dd7c1992653e0d9cc534f7914b6f583b022e0a7aa8a430f407932f9a6806f0fc64d61e78d5ae01aa8f8233196719d44da2c50a2d1cfa3f7abb7487b3567a4f0456aa6667154c6749b1")] diff --git a/ICSharpCode.SharpZipLib/BZip2/BZip2.cs b/ICSharpCode.SharpZipLib/BZip2/BZip2.cs new file mode 100644 index 000000000000..4bd48b0357c3 --- /dev/null +++ b/ICSharpCode.SharpZipLib/BZip2/BZip2.cs @@ -0,0 +1,79 @@ +using System; +using System.IO; + +namespace ICSharpCode.SharpZipLib.BZip2 +{ + /// + /// An example class to demonstrate compression and decompression of BZip2 streams. + /// + public static class BZip2 + { + /// + /// Decompress the input writing + /// uncompressed data to the output stream + /// + /// The readable stream containing data to decompress. + /// The output stream to receive the decompressed data. + /// Both streams are closed on completion if true. + public static void Decompress(Stream inStream, Stream outStream, bool isStreamOwner) + { + if (inStream == null) + throw new ArgumentNullException(nameof(inStream)); + + if (outStream == null) + throw new ArgumentNullException(nameof(outStream)); + + try + { + using (BZip2InputStream bzipInput = new BZip2InputStream(inStream)) + { + bzipInput.IsStreamOwner = isStreamOwner; + Core.StreamUtils.Copy(bzipInput, outStream, new byte[4096]); + } + } + finally + { + if (isStreamOwner) + { + // inStream is closed by the BZip2InputStream if stream owner + outStream.Dispose(); + } + } + } + + /// + /// Compress the input stream sending + /// result data to output stream + /// + /// The readable stream to compress. + /// The output stream to receive the compressed data. + /// Both streams are closed on completion if true. + /// Block size acts as compression level (1 to 9) with 1 giving + /// the lowest compression and 9 the highest. + public static void Compress(Stream inStream, Stream outStream, bool isStreamOwner, int level) + { + if (inStream == null) + throw new ArgumentNullException(nameof(inStream)); + + if (outStream == null) + throw new ArgumentNullException(nameof(outStream)); + + try + { + using (BZip2OutputStream bzipOutput = new BZip2OutputStream(outStream, level)) + { + bzipOutput.IsStreamOwner = isStreamOwner; + Core.StreamUtils.Copy(inStream, bzipOutput, new byte[4096]); + } + } + finally + { + if (isStreamOwner) + { + // outStream is closed by the BZip2OutputStream if stream owner + inStream.Dispose(); + } + } + } + } +} diff --git a/ICSharpCode.SharpZipLib/BZip2/BZip2Constants.cs b/ICSharpCode.SharpZipLib/BZip2/BZip2Constants.cs new file mode 100644 index 000000000000..52fb8ad20f16 --- /dev/null +++ b/ICSharpCode.SharpZipLib/BZip2/BZip2Constants.cs @@ -0,0 +1,117 @@ +namespace ICSharpCode.SharpZipLib.BZip2 +{ + /// + /// Defines internal values for both compression and decompression + /// + internal static class BZip2Constants + { + /// + /// Random numbers used to randomise repetitive blocks + /// + public readonly static int[] RandomNumbers = { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 + }; + + /// + /// When multiplied by compression parameter (1-9) gives the block size for compression + /// 9 gives the best compression but uses the most memory. + /// + public const int BaseBlockSize = 100000; + + /// + /// Backend constant + /// + public const int MaximumAlphaSize = 258; + + /// + /// Backend constant + /// + public const int MaximumCodeLength = 23; + + /// + /// Backend constant + /// + public const int RunA = 0; + + /// + /// Backend constant + /// + public const int RunB = 1; + + /// + /// Backend constant + /// + public const int GroupCount = 6; + + /// + /// Backend constant + /// + public const int GroupSize = 50; + + /// + /// Backend constant + /// + public const int NumberOfIterations = 4; + + /// + /// Backend constant + /// + public const int MaximumSelectors = (2 + (900000 / GroupSize)); + + /// + /// Backend constant + /// + public const int OvershootBytes = 20; + } +} diff --git a/ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs b/ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs new file mode 100644 index 000000000000..111d21cdcde0 --- /dev/null +++ b/ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs @@ -0,0 +1,54 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib.BZip2 +{ + /// + /// BZip2Exception represents exceptions specific to BZip2 classes and code. + /// + [Serializable] + public class BZip2Exception : SharpZipBaseException + { + /// + /// Initialise a new instance of . + /// + public BZip2Exception() + { + } + + /// + /// Initialise a new instance of with its message string. + /// + /// A that describes the error. + public BZip2Exception(string message) + : base(message) + { + } + + /// + /// Initialise a new instance of . + /// + /// A that describes the error. + /// The that caused this exception. + public BZip2Exception(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the BZip2Exception class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected BZip2Exception(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/BZip2/BZip2InputStream.cs b/ICSharpCode.SharpZipLib/BZip2/BZip2InputStream.cs new file mode 100644 index 000000000000..3948b4e4cc15 --- /dev/null +++ b/ICSharpCode.SharpZipLib/BZip2/BZip2InputStream.cs @@ -0,0 +1,1053 @@ +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER + #define VECTORIZE_MEMORY_MOVE +#endif + +using ICSharpCode.SharpZipLib.Checksum; +using System; +using System.IO; + +namespace ICSharpCode.SharpZipLib.BZip2 +{ + /// + /// An input stream that decompresses files in the BZip2 format + /// + public class BZip2InputStream : Stream + { + #region Constants + + private const int START_BLOCK_STATE = 1; + private const int RAND_PART_A_STATE = 2; + private const int RAND_PART_B_STATE = 3; + private const int RAND_PART_C_STATE = 4; + private const int NO_RAND_PART_A_STATE = 5; + private const int NO_RAND_PART_B_STATE = 6; + private const int NO_RAND_PART_C_STATE = 7; + +#if VECTORIZE_MEMORY_MOVE + private static readonly int VectorSize = System.Numerics.Vector.Count; +#endif // VECTORIZE_MEMORY_MOVE + +#endregion Constants + + #region Instance Fields + + /*-- + index of the last char in the block, so + the block size == last + 1. + --*/ + private int last; + + /*-- + index in zptr[] of original string after sorting. + --*/ + private int origPtr; + + /*-- + always: in the range 0 .. 9. + The current block size is 100000 * this number. + --*/ + private int blockSize100k; + + private bool blockRandomised; + + private int bsBuff; + private int bsLive; + private IChecksum mCrc = new BZip2Crc(); + + private bool[] inUse = new bool[256]; + private int nInUse; + + private byte[] seqToUnseq = new byte[256]; + private byte[] unseqToSeq = new byte[256]; + + private byte[] selector = new byte[BZip2Constants.MaximumSelectors]; + private byte[] selectorMtf = new byte[BZip2Constants.MaximumSelectors]; + + private int[] tt; + private byte[] ll8; + + /*-- + freq table collected to save a pass over the data + during decompression. + --*/ + private int[] unzftab = new int[256]; + + private int[][] limit = new int[BZip2Constants.GroupCount][]; + private int[][] baseArray = new int[BZip2Constants.GroupCount][]; + private int[][] perm = new int[BZip2Constants.GroupCount][]; + private int[] minLens = new int[BZip2Constants.GroupCount]; + + private readonly Stream baseStream; + private bool streamEnd; + + private int currentChar = -1; + + private int currentState = START_BLOCK_STATE; + + private int storedBlockCRC, storedCombinedCRC; + private int computedBlockCRC; + private uint computedCombinedCRC; + + private int count, chPrev, ch2; + private int tPos; + private int rNToGo; + private int rTPos; + private int i2, j2; + private byte z; + + #endregion Instance Fields + + /// + /// Construct instance for reading from stream + /// + /// Data source + public BZip2InputStream(Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + // init arrays + for (int i = 0; i < BZip2Constants.GroupCount; ++i) + { + limit[i] = new int[BZip2Constants.MaximumAlphaSize]; + baseArray[i] = new int[BZip2Constants.MaximumAlphaSize]; + perm[i] = new int[BZip2Constants.MaximumAlphaSize]; + } + + baseStream = stream; + bsLive = 0; + bsBuff = 0; + Initialize(); + InitBlock(); + SetupBlock(); + } + + /// + /// Get/set flag indicating ownership of underlying stream. + /// When the flag is true will close the underlying stream also. + /// + public bool IsStreamOwner { get; set; } = true; + + #region Stream Overrides + + /// + /// Gets a value indicating if the stream supports reading + /// + public override bool CanRead + { + get + { + return baseStream.CanRead; + } + } + + /// + /// Gets a value indicating whether the current stream supports seeking. + /// + public override bool CanSeek + { + get + { + return false; + } + } + + /// + /// Gets a value indicating whether the current stream supports writing. + /// This property always returns false + /// + public override bool CanWrite + { + get + { + return false; + } + } + + /// + /// Gets the length in bytes of the stream. + /// + public override long Length + { + get + { + return baseStream.Length; + } + } + + /// + /// Gets the current position of the stream. + /// Setting the position is not supported and will throw a NotSupportException. + /// + /// Any attempt to set the position. + public override long Position + { + get + { + return baseStream.Position; + } + set + { + throw new NotSupportedException("BZip2InputStream position cannot be set"); + } + } + + /// + /// Flushes the stream. + /// + public override void Flush() + { + baseStream.Flush(); + } + + /// + /// Set the streams position. This operation is not supported and will throw a NotSupportedException + /// + /// A byte offset relative to the parameter. + /// A value of type indicating the reference point used to obtain the new position. + /// The new position of the stream. + /// Any access + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("BZip2InputStream Seek not supported"); + } + + /// + /// Sets the length of this stream to the given value. + /// This operation is not supported and will throw a NotSupportedExceptionortedException + /// + /// The new length for the stream. + /// Any access + public override void SetLength(long value) + { + throw new NotSupportedException("BZip2InputStream SetLength not supported"); + } + + /// + /// Writes a block of bytes to this stream using data from a buffer. + /// This operation is not supported and will throw a NotSupportedException + /// + /// The buffer to source data from. + /// The offset to start obtaining data from. + /// The number of bytes of data to write. + /// Any access + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException("BZip2InputStream Write not supported"); + } + + /// + /// Writes a byte to the current position in the file stream. + /// This operation is not supported and will throw a NotSupportedException + /// + /// The value to write. + /// Any access + public override void WriteByte(byte value) + { + throw new NotSupportedException("BZip2InputStream WriteByte not supported"); + } + + /// + /// Read a sequence of bytes and advances the read position by one byte. + /// + /// Array of bytes to store values in + /// Offset in array to begin storing data + /// The maximum number of bytes to read + /// The total number of bytes read into the buffer. This might be less + /// than the number of bytes requested if that number of bytes are not + /// currently available or zero if the end of the stream is reached. + /// + public override int Read(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + for (int i = 0; i < count; ++i) + { + int rb = ReadByte(); + if (rb == -1) + { + return i; + } + buffer[offset + i] = (byte)rb; + } + return count; + } + + /// + /// Closes the stream, releasing any associated resources. + /// + protected override void Dispose(bool disposing) + { + if (disposing && IsStreamOwner) + { + baseStream.Dispose(); + } + } + + /// + /// Read a byte from stream advancing position + /// + /// byte read or -1 on end of stream + public override int ReadByte() + { + if (streamEnd) + { + return -1; // ok + } + + int retChar = currentChar; + switch (currentState) + { + case RAND_PART_B_STATE: + SetupRandPartB(); + break; + + case RAND_PART_C_STATE: + SetupRandPartC(); + break; + + case NO_RAND_PART_B_STATE: + SetupNoRandPartB(); + break; + + case NO_RAND_PART_C_STATE: + SetupNoRandPartC(); + break; + + case START_BLOCK_STATE: + case NO_RAND_PART_A_STATE: + case RAND_PART_A_STATE: + break; + } + return retChar; + } + + #endregion Stream Overrides + + private void MakeMaps() + { + nInUse = 0; + for (int i = 0; i < 256; ++i) + { + if (inUse[i]) + { + seqToUnseq[nInUse] = (byte)i; + unseqToSeq[i] = (byte)nInUse; + nInUse++; + } + } + } + + private void Initialize() + { + char magic1 = BsGetUChar(); + char magic2 = BsGetUChar(); + + char magic3 = BsGetUChar(); + char magic4 = BsGetUChar(); + + if (magic1 != 'B' || magic2 != 'Z' || magic3 != 'h' || magic4 < '1' || magic4 > '9') + { + streamEnd = true; + return; + } + + SetDecompressStructureSizes(magic4 - '0'); + computedCombinedCRC = 0; + } + + private void InitBlock() + { + char magic1 = BsGetUChar(); + char magic2 = BsGetUChar(); + char magic3 = BsGetUChar(); + char magic4 = BsGetUChar(); + char magic5 = BsGetUChar(); + char magic6 = BsGetUChar(); + + if (magic1 == 0x17 && magic2 == 0x72 && magic3 == 0x45 && magic4 == 0x38 && magic5 == 0x50 && magic6 == 0x90) + { + Complete(); + return; + } + + if (magic1 != 0x31 || magic2 != 0x41 || magic3 != 0x59 || magic4 != 0x26 || magic5 != 0x53 || magic6 != 0x59) + { + BadBlockHeader(); + streamEnd = true; + return; + } + + storedBlockCRC = BsGetInt32(); + + blockRandomised = (BsR(1) == 1); + + GetAndMoveToFrontDecode(); + + mCrc.Reset(); + currentState = START_BLOCK_STATE; + } + + private void EndBlock() + { + computedBlockCRC = (int)mCrc.Value; + + // -- A bad CRC is considered a fatal error. -- + if (storedBlockCRC != computedBlockCRC) + { + CrcError(); + } + + // 1528150659 + computedCombinedCRC = ((computedCombinedCRC << 1) & 0xFFFFFFFF) | (computedCombinedCRC >> 31); + computedCombinedCRC = computedCombinedCRC ^ (uint)computedBlockCRC; + } + + private void Complete() + { + storedCombinedCRC = BsGetInt32(); + if (storedCombinedCRC != (int)computedCombinedCRC) + { + CrcError(); + } + + streamEnd = true; + } + + private void FillBuffer() + { + int thech = 0; + + try + { + thech = baseStream.ReadByte(); + } + catch (Exception) + { + CompressedStreamEOF(); + } + + if (thech == -1) + { + CompressedStreamEOF(); + } + + bsBuff = (bsBuff << 8) | (thech & 0xFF); + bsLive += 8; + } + + private int BsR(int n) + { + while (bsLive < n) + { + FillBuffer(); + } + + int v = (bsBuff >> (bsLive - n)) & ((1 << n) - 1); + bsLive -= n; + return v; + } + + private char BsGetUChar() + { + return (char)BsR(8); + } + + private int BsGetIntVS(int numBits) + { + return BsR(numBits); + } + + private int BsGetInt32() + { + int result = BsR(8); + result = (result << 8) | BsR(8); + result = (result << 8) | BsR(8); + result = (result << 8) | BsR(8); + return result; + } + + private void RecvDecodingTables() + { + char[][] len = new char[BZip2Constants.GroupCount][]; + for (int i = 0; i < BZip2Constants.GroupCount; ++i) + { + len[i] = new char[BZip2Constants.MaximumAlphaSize]; + } + + bool[] inUse16 = new bool[16]; + + //--- Receive the mapping table --- + for (int i = 0; i < 16; i++) + { + inUse16[i] = (BsR(1) == 1); + } + + for (int i = 0; i < 16; i++) + { + if (inUse16[i]) + { + for (int j = 0; j < 16; j++) + { + inUse[i * 16 + j] = (BsR(1) == 1); + } + } + else + { + for (int j = 0; j < 16; j++) + { + inUse[i * 16 + j] = false; + } + } + } + + MakeMaps(); + int alphaSize = nInUse + 2; + + //--- Now the selectors --- + int nGroups = BsR(3); + int nSelectors = BsR(15); + + for (int i = 0; i < nSelectors; i++) + { + int j = 0; + while (BsR(1) == 1) + { + j++; + } + selectorMtf[i] = (byte)j; + } + + //--- Undo the MTF values for the selectors. --- + byte[] pos = new byte[BZip2Constants.GroupCount]; + for (int v = 0; v < nGroups; v++) + { + pos[v] = (byte)v; + } + + for (int i = 0; i < nSelectors; i++) + { + int v = selectorMtf[i]; + byte tmp = pos[v]; + while (v > 0) + { + pos[v] = pos[v - 1]; + v--; + } + pos[0] = tmp; + selector[i] = tmp; + } + + //--- Now the coding tables --- + for (int t = 0; t < nGroups; t++) + { + int curr = BsR(5); + for (int i = 0; i < alphaSize; i++) + { + while (BsR(1) == 1) + { + if (BsR(1) == 0) + { + curr++; + } + else + { + curr--; + } + } + len[t][i] = (char)curr; + } + } + + //--- Create the Huffman decoding tables --- + for (int t = 0; t < nGroups; t++) + { + int minLen = 32; + int maxLen = 0; + for (int i = 0; i < alphaSize; i++) + { + maxLen = Math.Max(maxLen, len[t][i]); + minLen = Math.Min(minLen, len[t][i]); + } + HbCreateDecodeTables(limit[t], baseArray[t], perm[t], len[t], minLen, maxLen, alphaSize); + minLens[t] = minLen; + } + } + + private void GetAndMoveToFrontDecode() + { + byte[] yy = new byte[256]; + int nextSym; + + int limitLast = BZip2Constants.BaseBlockSize * blockSize100k; + origPtr = BsGetIntVS(24); + + RecvDecodingTables(); + int EOB = nInUse + 1; + int groupNo = -1; + int groupPos = 0; + + /*-- + Setting up the unzftab entries here is not strictly + necessary, but it does save having to do it later + in a separate pass, and so saves a block's worth of + cache misses. + --*/ + for (int i = 0; i <= 255; i++) + { + unzftab[i] = 0; + } + + for (int i = 0; i <= 255; i++) + { + yy[i] = (byte)i; + } + + last = -1; + + if (groupPos == 0) + { + groupNo++; + groupPos = BZip2Constants.GroupSize; + } + + groupPos--; + int zt = selector[groupNo]; + int zn = minLens[zt]; + int zvec = BsR(zn); + int zj; + + while (zvec > limit[zt][zn]) + { + if (zn > 20) + { // the longest code + throw new BZip2Exception("Bzip data error"); + } + zn++; + while (bsLive < 1) + { + FillBuffer(); + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + zvec = (zvec << 1) | zj; + } + if (zvec - baseArray[zt][zn] < 0 || zvec - baseArray[zt][zn] >= BZip2Constants.MaximumAlphaSize) + { + throw new BZip2Exception("Bzip data error"); + } + nextSym = perm[zt][zvec - baseArray[zt][zn]]; + + while (true) + { + if (nextSym == EOB) + { + break; + } + + if (nextSym == BZip2Constants.RunA || nextSym == BZip2Constants.RunB) + { + int s = -1; + int n = 1; + do + { + if (nextSym == BZip2Constants.RunA) + { + s += (0 + 1) * n; + } + else if (nextSym == BZip2Constants.RunB) + { + s += (1 + 1) * n; + } + + n <<= 1; + + if (groupPos == 0) + { + groupNo++; + groupPos = BZip2Constants.GroupSize; + } + + groupPos--; + + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = BsR(zn); + + while (zvec > limit[zt][zn]) + { + zn++; + while (bsLive < 1) + { + FillBuffer(); + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - baseArray[zt][zn]]; + } while (nextSym == BZip2Constants.RunA || nextSym == BZip2Constants.RunB); + + s++; + byte ch = seqToUnseq[yy[0]]; + unzftab[ch] += s; + + while (s > 0) + { + last++; + ll8[last] = ch; + s--; + } + + if (last >= limitLast) + { + BlockOverrun(); + } + continue; + } + else + { + last++; + if (last >= limitLast) + { + BlockOverrun(); + } + + byte tmp = yy[nextSym - 1]; + unzftab[seqToUnseq[tmp]]++; + ll8[last] = seqToUnseq[tmp]; + + var j = nextSym - 1; + +#if VECTORIZE_MEMORY_MOVE + // This is vectorized memory move. Going from the back, we're taking chunks of array + // and write them at the new location shifted by one. Since chunks are VectorSize long, + // at the end we have to move "tail" (or head actually) of the array using a plain loop. + // If System.Numerics.Vector API is not available, the plain loop is used to do the whole copying. + + while(j >= VectorSize) + { + var arrayPart = new System.Numerics.Vector(yy, j - VectorSize); + arrayPart.CopyTo(yy, j - VectorSize + 1); + j -= VectorSize; + } +#endif // VECTORIZE_MEMORY_MOVE + + while(j > 0) + { + yy[j] = yy[--j]; + } + + yy[0] = tmp; + + if (groupPos == 0) + { + groupNo++; + groupPos = BZip2Constants.GroupSize; + } + + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = BsR(zn); + while (zvec > limit[zt][zn]) + { + zn++; + while (bsLive < 1) + { + FillBuffer(); + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - baseArray[zt][zn]]; + continue; + } + } + } + + private void SetupBlock() + { + int[] cftab = new int[257]; + + cftab[0] = 0; + Array.Copy(unzftab, 0, cftab, 1, 256); + + for (int i = 1; i <= 256; i++) + { + cftab[i] += cftab[i - 1]; + } + + for (int i = 0; i <= last; i++) + { + byte ch = ll8[i]; + tt[cftab[ch]] = i; + cftab[ch]++; + } + + cftab = null; + + tPos = tt[origPtr]; + + count = 0; + i2 = 0; + ch2 = 256; /*-- not a char and not EOF --*/ + + if (blockRandomised) + { + rNToGo = 0; + rTPos = 0; + SetupRandPartA(); + } + else + { + SetupNoRandPartA(); + } + } + + private void SetupRandPartA() + { + if (i2 <= last) + { + chPrev = ch2; + ch2 = ll8[tPos]; + tPos = tt[tPos]; + if (rNToGo == 0) + { + rNToGo = BZip2Constants.RandomNumbers[rTPos]; + rTPos++; + if (rTPos == 512) + { + rTPos = 0; + } + } + rNToGo--; + ch2 ^= (int)((rNToGo == 1) ? 1 : 0); + i2++; + + currentChar = ch2; + currentState = RAND_PART_B_STATE; + mCrc.Update(ch2); + } + else + { + EndBlock(); + InitBlock(); + SetupBlock(); + } + } + + private void SetupNoRandPartA() + { + if (i2 <= last) + { + chPrev = ch2; + ch2 = ll8[tPos]; + tPos = tt[tPos]; + i2++; + + currentChar = ch2; + currentState = NO_RAND_PART_B_STATE; + mCrc.Update(ch2); + } + else + { + EndBlock(); + InitBlock(); + SetupBlock(); + } + } + + private void SetupRandPartB() + { + if (ch2 != chPrev) + { + currentState = RAND_PART_A_STATE; + count = 1; + SetupRandPartA(); + } + else + { + count++; + if (count >= 4) + { + z = ll8[tPos]; + tPos = tt[tPos]; + if (rNToGo == 0) + { + rNToGo = BZip2Constants.RandomNumbers[rTPos]; + rTPos++; + if (rTPos == 512) + { + rTPos = 0; + } + } + rNToGo--; + z ^= (byte)((rNToGo == 1) ? 1 : 0); + j2 = 0; + currentState = RAND_PART_C_STATE; + SetupRandPartC(); + } + else + { + currentState = RAND_PART_A_STATE; + SetupRandPartA(); + } + } + } + + private void SetupRandPartC() + { + if (j2 < (int)z) + { + currentChar = ch2; + mCrc.Update(ch2); + j2++; + } + else + { + currentState = RAND_PART_A_STATE; + i2++; + count = 0; + SetupRandPartA(); + } + } + + private void SetupNoRandPartB() + { + if (ch2 != chPrev) + { + currentState = NO_RAND_PART_A_STATE; + count = 1; + SetupNoRandPartA(); + } + else + { + count++; + if (count >= 4) + { + z = ll8[tPos]; + tPos = tt[tPos]; + currentState = NO_RAND_PART_C_STATE; + j2 = 0; + SetupNoRandPartC(); + } + else + { + currentState = NO_RAND_PART_A_STATE; + SetupNoRandPartA(); + } + } + } + + private void SetupNoRandPartC() + { + if (j2 < (int)z) + { + currentChar = ch2; + mCrc.Update(ch2); + j2++; + } + else + { + currentState = NO_RAND_PART_A_STATE; + i2++; + count = 0; + SetupNoRandPartA(); + } + } + + private void SetDecompressStructureSizes(int newSize100k) + { + if (!(0 <= newSize100k && newSize100k <= 9 && 0 <= blockSize100k && blockSize100k <= 9)) + { + throw new BZip2Exception("Invalid block size"); + } + + blockSize100k = newSize100k; + + if (newSize100k == 0) + { + return; + } + + int n = BZip2Constants.BaseBlockSize * newSize100k; + ll8 = new byte[n]; + tt = new int[n]; + } + + private static void CompressedStreamEOF() + { + throw new EndOfStreamException("BZip2 input stream end of compressed stream"); + } + + private static void BlockOverrun() + { + throw new BZip2Exception("BZip2 input stream block overrun"); + } + + private static void BadBlockHeader() + { + throw new BZip2Exception("BZip2 input stream bad block header"); + } + + private static void CrcError() + { + throw new BZip2Exception("BZip2 input stream crc error"); + } + + private static void HbCreateDecodeTables(int[] limit, int[] baseArray, int[] perm, char[] length, int minLen, int maxLen, int alphaSize) + { + int pp = 0; + + for (int i = minLen; i <= maxLen; ++i) + { + for (int j = 0; j < alphaSize; ++j) + { + if (length[j] == i) + { + perm[pp] = j; + ++pp; + } + } + } + + for (int i = 0; i < BZip2Constants.MaximumCodeLength; i++) + { + baseArray[i] = 0; + } + + for (int i = 0; i < alphaSize; i++) + { + ++baseArray[length[i] + 1]; + } + + for (int i = 1; i < BZip2Constants.MaximumCodeLength; i++) + { + baseArray[i] += baseArray[i - 1]; + } + + for (int i = 0; i < BZip2Constants.MaximumCodeLength; i++) + { + limit[i] = 0; + } + + int vec = 0; + + for (int i = minLen; i <= maxLen; i++) + { + vec += (baseArray[i + 1] - baseArray[i]); + limit[i] = vec - 1; + vec <<= 1; + } + + for (int i = minLen + 1; i <= maxLen; i++) + { + baseArray[i] = ((limit[i - 1] + 1) << 1) - baseArray[i]; + } + } + } +} diff --git a/ICSharpCode.SharpZipLib/BZip2/BZip2OutputStream.cs b/ICSharpCode.SharpZipLib/BZip2/BZip2OutputStream.cs new file mode 100644 index 000000000000..f331ec657410 --- /dev/null +++ b/ICSharpCode.SharpZipLib/BZip2/BZip2OutputStream.cs @@ -0,0 +1,2033 @@ +using ICSharpCode.SharpZipLib.Checksum; +using System; +using System.IO; + +namespace ICSharpCode.SharpZipLib.BZip2 +{ + /// + /// An output stream that compresses into the BZip2 format + /// including file header chars into another stream. + /// + public class BZip2OutputStream : Stream + { + #region Constants + + private const int SETMASK = (1 << 21); + private const int CLEARMASK = (~SETMASK); + private const int GREATER_ICOST = 15; + private const int LESSER_ICOST = 0; + private const int SMALL_THRESH = 20; + private const int DEPTH_THRESH = 10; + + /*-- + If you are ever unlucky/improbable enough + to get a stack overflow whilst sorting, + increase the following constant and try + again. In practice I have never seen the + stack go above 27 elems, so the following + limit seems very generous. + --*/ + private const int QSORT_STACK_SIZE = 1000; + + /*-- + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. + --*/ + + private readonly int[] increments = { + 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 + }; + + #endregion Constants + + #region Instance Fields + + /*-- + index of the last char in the block, so + the block size == last + 1. + --*/ + private int last; + + /*-- + index in zptr[] of original string after sorting. + --*/ + private int origPtr; + + /*-- + always: in the range 0 .. 9. + The current block size is 100000 * this number. + --*/ + private int blockSize100k; + + private bool blockRandomised; + + private int bytesOut; + private int bsBuff; + private int bsLive; + private IChecksum mCrc = new BZip2Crc(); + + private bool[] inUse = new bool[256]; + private int nInUse; + + private char[] seqToUnseq = new char[256]; + private char[] unseqToSeq = new char[256]; + + private char[] selector = new char[BZip2Constants.MaximumSelectors]; + private char[] selectorMtf = new char[BZip2Constants.MaximumSelectors]; + + private byte[] block; + private int[] quadrant; + private int[] zptr; + private short[] szptr; + private int[] ftab; + + private int nMTF; + + private int[] mtfFreq = new int[BZip2Constants.MaximumAlphaSize]; + + /* + * Used when sorting. If too many long comparisons + * happen, we stop sorting, randomise the block + * slightly, and try again. + */ + private int workFactor; + private int workDone; + private int workLimit; + private bool firstAttempt; + private int nBlocksRandomised; + + private int currentChar = -1; + private int runLength; + private uint blockCRC, combinedCRC; + private int allowableBlockSize; + private readonly Stream baseStream; + private bool disposed_; + + #endregion Instance Fields + + /// + /// Construct a default output stream with maximum block size + /// + /// The stream to write BZip data onto. + public BZip2OutputStream(Stream stream) : this(stream, 9) + { + } + + /// + /// Initialise a new instance of the + /// for the specified stream, using the given blocksize. + /// + /// The stream to write compressed data to. + /// The block size to use. + /// + /// Valid block sizes are in the range 1..9, with 1 giving + /// the lowest compression and 9 the highest. + /// + public BZip2OutputStream(Stream stream, int blockSize) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + baseStream = stream; + bsLive = 0; + bsBuff = 0; + bytesOut = 0; + + workFactor = 50; + if (blockSize > 9) + { + blockSize = 9; + } + + if (blockSize < 1) + { + blockSize = 1; + } + blockSize100k = blockSize; + AllocateCompressStructures(); + Initialize(); + InitBlock(); + } + + /// + /// Ensures that resources are freed and other cleanup operations + /// are performed when the garbage collector reclaims the BZip2OutputStream. + /// + ~BZip2OutputStream() + { + Dispose(false); + } + + /// + /// Gets or sets a flag indicating ownership of underlying stream. + /// When the flag is true will close the underlying stream also. + /// + /// The default value is true. + public bool IsStreamOwner { get; set; } = true; + + /// + /// Gets a value indicating whether the current stream supports reading + /// + public override bool CanRead + { + get + { + return false; + } + } + + /// + /// Gets a value indicating whether the current stream supports seeking + /// + public override bool CanSeek + { + get + { + return false; + } + } + + /// + /// Gets a value indicating whether the current stream supports writing + /// + public override bool CanWrite + { + get + { + return baseStream.CanWrite; + } + } + + /// + /// Gets the length in bytes of the stream + /// + public override long Length + { + get + { + return baseStream.Length; + } + } + + /// + /// Gets or sets the current position of this stream. + /// + public override long Position + { + get + { + return baseStream.Position; + } + set + { + throw new NotSupportedException("BZip2OutputStream position cannot be set"); + } + } + + /// + /// Sets the current position of this stream to the given value. + /// + /// The point relative to the offset from which to being seeking. + /// The reference point from which to begin seeking. + /// The new position in the stream. + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("BZip2OutputStream Seek not supported"); + } + + /// + /// Sets the length of this stream to the given value. + /// + /// The new stream length. + public override void SetLength(long value) + { + throw new NotSupportedException("BZip2OutputStream SetLength not supported"); + } + + /// + /// Read a byte from the stream advancing the position. + /// + /// The byte read cast to an int; -1 if end of stream. + public override int ReadByte() + { + throw new NotSupportedException("BZip2OutputStream ReadByte not supported"); + } + + /// + /// Read a block of bytes + /// + /// The buffer to read into. + /// The offset in the buffer to start storing data at. + /// The maximum number of bytes to read. + /// The total number of bytes read. This might be less than the number of bytes + /// requested if that number of bytes are not currently available, or zero + /// if the end of the stream is reached. + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException("BZip2OutputStream Read not supported"); + } + + /// + /// Write a block of bytes to the stream + /// + /// The buffer containing data to write. + /// The offset of the first byte to write. + /// The number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (buffer.Length - offset < count) + { + throw new ArgumentException("Offset/count out of range"); + } + + for (int i = 0; i < count; ++i) + { + WriteByte(buffer[offset + i]); + } + } + + /// + /// Write a byte to the stream. + /// + /// The byte to write to the stream. + public override void WriteByte(byte value) + { + int b = (256 + value) % 256; + if (currentChar != -1) + { + if (currentChar == b) + { + runLength++; + if (runLength > 254) + { + WriteRun(); + currentChar = -1; + runLength = 0; + } + } + else + { + WriteRun(); + runLength = 1; + currentChar = b; + } + } + else + { + currentChar = b; + runLength++; + } + } + + private void MakeMaps() + { + nInUse = 0; + for (int i = 0; i < 256; i++) + { + if (inUse[i]) + { + seqToUnseq[nInUse] = (char)i; + unseqToSeq[i] = (char)nInUse; + nInUse++; + } + } + } + + /// + /// Get the number of bytes written to output. + /// + private void WriteRun() + { + if (last < allowableBlockSize) + { + inUse[currentChar] = true; + for (int i = 0; i < runLength; i++) + { + mCrc.Update(currentChar); + } + + switch (runLength) + { + case 1: + last++; + block[last + 1] = (byte)currentChar; + break; + + case 2: + last++; + block[last + 1] = (byte)currentChar; + last++; + block[last + 1] = (byte)currentChar; + break; + + case 3: + last++; + block[last + 1] = (byte)currentChar; + last++; + block[last + 1] = (byte)currentChar; + last++; + block[last + 1] = (byte)currentChar; + break; + + default: + inUse[runLength - 4] = true; + last++; + block[last + 1] = (byte)currentChar; + last++; + block[last + 1] = (byte)currentChar; + last++; + block[last + 1] = (byte)currentChar; + last++; + block[last + 1] = (byte)currentChar; + last++; + block[last + 1] = (byte)(runLength - 4); + break; + } + } + else + { + EndBlock(); + InitBlock(); + WriteRun(); + } + } + + /// + /// Get the number of bytes written to the output. + /// + public int BytesWritten + { + get { return bytesOut; } + } + + /// + /// Releases the unmanaged resources used by the and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + override protected void Dispose(bool disposing) + { + try + { + try + { + base.Dispose(disposing); + if (!disposed_) + { + disposed_ = true; + + if (runLength > 0) + { + WriteRun(); + } + + currentChar = -1; + EndBlock(); + EndCompression(); + Flush(); + } + } + finally + { + if (disposing) + { + if (IsStreamOwner) + { + baseStream.Dispose(); + } + } + } + } + catch + { + } + } + + /// + /// Flush output buffers + /// + public override void Flush() + { + baseStream.Flush(); + } + + private void Initialize() + { + bytesOut = 0; + nBlocksRandomised = 0; + + /*--- Write header `magic' bytes indicating file-format == huffmanised, + followed by a digit indicating blockSize100k. + ---*/ + + BsPutUChar('B'); + BsPutUChar('Z'); + + BsPutUChar('h'); + BsPutUChar('0' + blockSize100k); + + combinedCRC = 0; + } + + private void InitBlock() + { + mCrc.Reset(); + last = -1; + + for (int i = 0; i < 256; i++) + { + inUse[i] = false; + } + + /*--- 20 is just a paranoia constant ---*/ + allowableBlockSize = BZip2Constants.BaseBlockSize * blockSize100k - 20; + } + + private void EndBlock() + { + if (last < 0) + { // dont do anything for empty files, (makes empty files compatible with original Bzip) + return; + } + + blockCRC = unchecked((uint)mCrc.Value); + combinedCRC = (combinedCRC << 1) | (combinedCRC >> 31); + combinedCRC ^= blockCRC; + + /*-- sort the block and establish position of original string --*/ + DoReversibleTransformation(); + + /*-- + A 6-byte block header, the value chosen arbitrarily + as 0x314159265359 :-). A 32 bit value does not really + give a strong enough guarantee that the value will not + appear by chance in the compressed datastream. Worst-case + probability of this event, for a 900k block, is about + 2.0e-3 for 32 bits, 1.0e-5 for 40 bits and 4.0e-8 for 48 bits. + For a compressed file of size 100Gb -- about 100000 blocks -- + only a 48-bit marker will do. NB: normal compression/ + decompression do *not* rely on these statistical properties. + They are only important when trying to recover blocks from + damaged files. + --*/ + BsPutUChar(0x31); + BsPutUChar(0x41); + BsPutUChar(0x59); + BsPutUChar(0x26); + BsPutUChar(0x53); + BsPutUChar(0x59); + + /*-- Now the block's CRC, so it is in a known place. --*/ + unchecked + { + BsPutint((int)blockCRC); + } + + /*-- Now a single bit indicating randomisation. --*/ + if (blockRandomised) + { + BsW(1, 1); + nBlocksRandomised++; + } + else + { + BsW(1, 0); + } + + /*-- Finally, block's contents proper. --*/ + MoveToFrontCodeAndSend(); + } + + private void EndCompression() + { + /*-- + Now another magic 48-bit number, 0x177245385090, to + indicate the end of the last block. (sqrt(pi), if + you want to know. I did want to use e, but it contains + too much repetition -- 27 18 28 18 28 46 -- for me + to feel statistically comfortable. Call me paranoid.) + --*/ + BsPutUChar(0x17); + BsPutUChar(0x72); + BsPutUChar(0x45); + BsPutUChar(0x38); + BsPutUChar(0x50); + BsPutUChar(0x90); + + unchecked + { + BsPutint((int)combinedCRC); + } + + BsFinishedWithStream(); + } + + private void BsFinishedWithStream() + { + while (bsLive > 0) + { + int ch = (bsBuff >> 24); + baseStream.WriteByte((byte)ch); // write 8-bit + bsBuff <<= 8; + bsLive -= 8; + bytesOut++; + } + } + + private void BsW(int n, int v) + { + while (bsLive >= 8) + { + int ch = (bsBuff >> 24); + unchecked { baseStream.WriteByte((byte)ch); } // write 8-bit + bsBuff <<= 8; + bsLive -= 8; + ++bytesOut; + } + bsBuff |= (v << (32 - bsLive - n)); + bsLive += n; + } + + private void BsPutUChar(int c) + { + BsW(8, c); + } + + private void BsPutint(int u) + { + BsW(8, (u >> 24) & 0xFF); + BsW(8, (u >> 16) & 0xFF); + BsW(8, (u >> 8) & 0xFF); + BsW(8, u & 0xFF); + } + + private void BsPutIntVS(int numBits, int c) + { + BsW(numBits, c); + } + + private void SendMTFValues() + { + char[][] len = new char[BZip2Constants.GroupCount][]; + for (int i = 0; i < BZip2Constants.GroupCount; ++i) + { + len[i] = new char[BZip2Constants.MaximumAlphaSize]; + } + + int gs, ge, totc, bt, bc, iter; + int nSelectors = 0, alphaSize, minLen, maxLen, selCtr; + int nGroups; + + alphaSize = nInUse + 2; + for (int t = 0; t < BZip2Constants.GroupCount; t++) + { + for (int v = 0; v < alphaSize; v++) + { + len[t][v] = (char)GREATER_ICOST; + } + } + + /*--- Decide how many coding tables to use ---*/ + if (nMTF <= 0) + { + Panic(); + } + + if (nMTF < 200) + { + nGroups = 2; + } + else if (nMTF < 600) + { + nGroups = 3; + } + else if (nMTF < 1200) + { + nGroups = 4; + } + else if (nMTF < 2400) + { + nGroups = 5; + } + else + { + nGroups = 6; + } + + /*--- Generate an initial set of coding tables ---*/ + int nPart = nGroups; + int remF = nMTF; + gs = 0; + while (nPart > 0) + { + int tFreq = remF / nPart; + int aFreq = 0; + ge = gs - 1; + while (aFreq < tFreq && ge < alphaSize - 1) + { + ge++; + aFreq += mtfFreq[ge]; + } + + if (ge > gs && nPart != nGroups && nPart != 1 && ((nGroups - nPart) % 2 == 1)) + { + aFreq -= mtfFreq[ge]; + ge--; + } + + for (int v = 0; v < alphaSize; v++) + { + if (v >= gs && v <= ge) + { + len[nPart - 1][v] = (char)LESSER_ICOST; + } + else + { + len[nPart - 1][v] = (char)GREATER_ICOST; + } + } + + nPart--; + gs = ge + 1; + remF -= aFreq; + } + + int[][] rfreq = new int[BZip2Constants.GroupCount][]; + for (int i = 0; i < BZip2Constants.GroupCount; ++i) + { + rfreq[i] = new int[BZip2Constants.MaximumAlphaSize]; + } + + int[] fave = new int[BZip2Constants.GroupCount]; + short[] cost = new short[BZip2Constants.GroupCount]; + /*--- + Iterate up to N_ITERS times to improve the tables. + ---*/ + for (iter = 0; iter < BZip2Constants.NumberOfIterations; ++iter) + { + for (int t = 0; t < nGroups; ++t) + { + fave[t] = 0; + } + + for (int t = 0; t < nGroups; ++t) + { + for (int v = 0; v < alphaSize; ++v) + { + rfreq[t][v] = 0; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (true) + { + /*--- Set group start & end marks. --*/ + if (gs >= nMTF) + { + break; + } + ge = gs + BZip2Constants.GroupSize - 1; + if (ge >= nMTF) + { + ge = nMTF - 1; + } + + /*-- + Calculate the cost of this group as coded + by each of the coding tables. + --*/ + for (int t = 0; t < nGroups; t++) + { + cost[t] = 0; + } + + if (nGroups == 6) + { + short cost0, cost1, cost2, cost3, cost4, cost5; + cost0 = cost1 = cost2 = cost3 = cost4 = cost5 = 0; + for (int i = gs; i <= ge; ++i) + { + short icv = szptr[i]; + cost0 += (short)len[0][icv]; + cost1 += (short)len[1][icv]; + cost2 += (short)len[2][icv]; + cost3 += (short)len[3][icv]; + cost4 += (short)len[4][icv]; + cost5 += (short)len[5][icv]; + } + cost[0] = cost0; + cost[1] = cost1; + cost[2] = cost2; + cost[3] = cost3; + cost[4] = cost4; + cost[5] = cost5; + } + else + { + for (int i = gs; i <= ge; ++i) + { + short icv = szptr[i]; + for (int t = 0; t < nGroups; t++) + { + cost[t] += (short)len[t][icv]; + } + } + } + + /*-- + Find the coding table which is best for this group, + and record its identity in the selector table. + --*/ + bc = 999999999; + bt = -1; + for (int t = 0; t < nGroups; ++t) + { + if (cost[t] < bc) + { + bc = cost[t]; + bt = t; + } + } + totc += bc; + fave[bt]++; + selector[nSelectors] = (char)bt; + nSelectors++; + + /*-- + Increment the symbol frequencies for the selected table. + --*/ + for (int i = gs; i <= ge; ++i) + { + ++rfreq[bt][szptr[i]]; + } + + gs = ge + 1; + } + + /*-- + Recompute the tables based on the accumulated frequencies. + --*/ + for (int t = 0; t < nGroups; ++t) + { + HbMakeCodeLengths(len[t], rfreq[t], alphaSize, 20); + } + } + + rfreq = null; + fave = null; + cost = null; + + if (!(nGroups < 8)) + { + Panic(); + } + + if (!(nSelectors < 32768 && nSelectors <= (2 + (900000 / BZip2Constants.GroupSize)))) + { + Panic(); + } + + /*--- Compute MTF values for the selectors. ---*/ + char[] pos = new char[BZip2Constants.GroupCount]; + char ll_i, tmp2, tmp; + + for (int i = 0; i < nGroups; i++) + { + pos[i] = (char)i; + } + + for (int i = 0; i < nSelectors; i++) + { + ll_i = selector[i]; + int j = 0; + tmp = pos[j]; + while (ll_i != tmp) + { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + } + pos[0] = tmp; + selectorMtf[i] = (char)j; + } + + int[][] code = new int[BZip2Constants.GroupCount][]; + + for (int i = 0; i < BZip2Constants.GroupCount; ++i) + { + code[i] = new int[BZip2Constants.MaximumAlphaSize]; + } + + /*--- Assign actual codes for the tables. --*/ + for (int t = 0; t < nGroups; t++) + { + minLen = 32; + maxLen = 0; + for (int i = 0; i < alphaSize; i++) + { + if (len[t][i] > maxLen) + { + maxLen = len[t][i]; + } + if (len[t][i] < minLen) + { + minLen = len[t][i]; + } + } + if (maxLen > 20) + { + Panic(); + } + if (minLen < 1) + { + Panic(); + } + HbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize); + } + + /*--- Transmit the mapping table. ---*/ + bool[] inUse16 = new bool[16]; + for (int i = 0; i < 16; ++i) + { + inUse16[i] = false; + for (int j = 0; j < 16; ++j) + { + if (inUse[i * 16 + j]) + { + inUse16[i] = true; + } + } + } + + for (int i = 0; i < 16; ++i) + { + if (inUse16[i]) + { + BsW(1, 1); + } + else + { + BsW(1, 0); + } + } + + for (int i = 0; i < 16; ++i) + { + if (inUse16[i]) + { + for (int j = 0; j < 16; ++j) + { + if (inUse[i * 16 + j]) + { + BsW(1, 1); + } + else + { + BsW(1, 0); + } + } + } + } + + /*--- Now the selectors. ---*/ + BsW(3, nGroups); + BsW(15, nSelectors); + for (int i = 0; i < nSelectors; ++i) + { + for (int j = 0; j < selectorMtf[i]; ++j) + { + BsW(1, 1); + } + BsW(1, 0); + } + + /*--- Now the coding tables. ---*/ + for (int t = 0; t < nGroups; ++t) + { + int curr = len[t][0]; + BsW(5, curr); + for (int i = 0; i < alphaSize; ++i) + { + while (curr < len[t][i]) + { + BsW(2, 2); + curr++; /* 10 */ + } + while (curr > len[t][i]) + { + BsW(2, 3); + curr--; /* 11 */ + } + BsW(1, 0); + } + } + + /*--- And finally, the block data proper ---*/ + selCtr = 0; + gs = 0; + while (true) + { + if (gs >= nMTF) + { + break; + } + ge = gs + BZip2Constants.GroupSize - 1; + if (ge >= nMTF) + { + ge = nMTF - 1; + } + + for (int i = gs; i <= ge; i++) + { + BsW(len[selector[selCtr]][szptr[i]], code[selector[selCtr]][szptr[i]]); + } + + gs = ge + 1; + ++selCtr; + } + if (!(selCtr == nSelectors)) + { + Panic(); + } + } + + private void MoveToFrontCodeAndSend() + { + BsPutIntVS(24, origPtr); + GenerateMTFValues(); + SendMTFValues(); + } + + private void SimpleSort(int lo, int hi, int d) + { + int i, j, h, bigN, hp; + int v; + + bigN = hi - lo + 1; + if (bigN < 2) + { + return; + } + + hp = 0; + while (increments[hp] < bigN) + { + hp++; + } + hp--; + + for (; hp >= 0; hp--) + { + h = increments[hp]; + + i = lo + h; + while (true) + { + /*-- copy 1 --*/ + if (i > hi) + break; + v = zptr[i]; + j = i; + while (FullGtU(zptr[j - h] + d, v + d)) + { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) + break; + } + zptr[j] = v; + i++; + + /*-- copy 2 --*/ + if (i > hi) + { + break; + } + v = zptr[i]; + j = i; + while (FullGtU(zptr[j - h] + d, v + d)) + { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) + { + break; + } + } + zptr[j] = v; + i++; + + /*-- copy 3 --*/ + if (i > hi) + { + break; + } + v = zptr[i]; + j = i; + while (FullGtU(zptr[j - h] + d, v + d)) + { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) + { + break; + } + } + zptr[j] = v; + i++; + + if (workDone > workLimit && firstAttempt) + { + return; + } + } + } + } + + private void Vswap(int p1, int p2, int n) + { + int temp = 0; + while (n > 0) + { + temp = zptr[p1]; + zptr[p1] = zptr[p2]; + zptr[p2] = temp; + p1++; + p2++; + n--; + } + } + + private void QSort3(int loSt, int hiSt, int dSt) + { + int unLo, unHi, ltLo, gtHi, med, n, m; + int lo, hi, d; + + StackElement[] stack = new StackElement[QSORT_STACK_SIZE]; + + int sp = 0; + + stack[sp].ll = loSt; + stack[sp].hh = hiSt; + stack[sp].dd = dSt; + sp++; + + while (sp > 0) + { + if (sp >= QSORT_STACK_SIZE) + { + Panic(); + } + + sp--; + lo = stack[sp].ll; + hi = stack[sp].hh; + d = stack[sp].dd; + + if (hi - lo < SMALL_THRESH || d > DEPTH_THRESH) + { + SimpleSort(lo, hi, d); + if (workDone > workLimit && firstAttempt) + { + return; + } + continue; + } + + med = Med3(block[zptr[lo] + d + 1], + block[zptr[hi] + d + 1], + block[zptr[(lo + hi) >> 1] + d + 1]); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (true) + { + while (true) + { + if (unLo > unHi) + { + break; + } + n = ((int)block[zptr[unLo] + d + 1]) - med; + if (n == 0) + { + int temp = zptr[unLo]; + zptr[unLo] = zptr[ltLo]; + zptr[ltLo] = temp; + ltLo++; + unLo++; + continue; + } + if (n > 0) + { + break; + } + unLo++; + } + + while (true) + { + if (unLo > unHi) + { + break; + } + n = ((int)block[zptr[unHi] + d + 1]) - med; + if (n == 0) + { + int temp = zptr[unHi]; + zptr[unHi] = zptr[gtHi]; + zptr[gtHi] = temp; + gtHi--; + unHi--; + continue; + } + if (n < 0) + { + break; + } + unHi--; + } + + if (unLo > unHi) + { + break; + } + + { + int temp = zptr[unLo]; + zptr[unLo] = zptr[unHi]; + zptr[unHi] = temp; + unLo++; + unHi--; + } + } + + if (gtHi < ltLo) + { + stack[sp].ll = lo; + stack[sp].hh = hi; + stack[sp].dd = d + 1; + sp++; + continue; + } + + n = ((ltLo - lo) < (unLo - ltLo)) ? (ltLo - lo) : (unLo - ltLo); + Vswap(lo, unLo - n, n); + m = ((hi - gtHi) < (gtHi - unHi)) ? (hi - gtHi) : (gtHi - unHi); + Vswap(unLo, hi - m + 1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + stack[sp].ll = lo; + stack[sp].hh = n; + stack[sp].dd = d; + sp++; + + stack[sp].ll = n + 1; + stack[sp].hh = m - 1; + stack[sp].dd = d + 1; + sp++; + + stack[sp].ll = m; + stack[sp].hh = hi; + stack[sp].dd = d; + sp++; + } + } + + private void MainSort() + { + int i, j, ss, sb; + int[] runningOrder = new int[256]; + int[] copy = new int[256]; + bool[] bigDone = new bool[256]; + int c1, c2; + int numQSorted; + + /*-- + In the various block-sized structures, live data runs + from 0 to last+NUM_OVERSHOOT_BYTES inclusive. First, + set up the overshoot area for block. + --*/ + + // if (verbosity >= 4) fprintf ( stderr, " sort initialise ...\n" ); + for (i = 0; i < BZip2Constants.OvershootBytes; i++) + { + block[last + i + 2] = block[(i % (last + 1)) + 1]; + } + for (i = 0; i <= last + BZip2Constants.OvershootBytes; i++) + { + quadrant[i] = 0; + } + + block[0] = (byte)(block[last + 1]); + + if (last < 4000) + { + /*-- + Use simpleSort(), since the full sorting mechanism + has quite a large constant overhead. + --*/ + for (i = 0; i <= last; i++) + { + zptr[i] = i; + } + firstAttempt = false; + workDone = workLimit = 0; + SimpleSort(0, last, 0); + } + else + { + numQSorted = 0; + for (i = 0; i <= 255; i++) + { + bigDone[i] = false; + } + for (i = 0; i <= 65536; i++) + { + ftab[i] = 0; + } + + c1 = block[0]; + for (i = 0; i <= last; i++) + { + c2 = block[i + 1]; + ftab[(c1 << 8) + c2]++; + c1 = c2; + } + + for (i = 1; i <= 65536; i++) + { + ftab[i] += ftab[i - 1]; + } + + c1 = block[1]; + for (i = 0; i < last; i++) + { + c2 = block[i + 2]; + j = (c1 << 8) + c2; + c1 = c2; + ftab[j]--; + zptr[ftab[j]] = i; + } + + j = ((block[last + 1]) << 8) + (block[1]); + ftab[j]--; + zptr[ftab[j]] = last; + + /*-- + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + --*/ + + for (i = 0; i <= 255; i++) + { + runningOrder[i] = i; + } + + int vv; + int h = 1; + do + { + h = 3 * h + 1; + } while (h <= 256); + do + { + h = h / 3; + for (i = h; i <= 255; i++) + { + vv = runningOrder[i]; + j = i; + while ((ftab[((runningOrder[j - h]) + 1) << 8] - ftab[(runningOrder[j - h]) << 8]) > (ftab[((vv) + 1) << 8] - ftab[(vv) << 8])) + { + runningOrder[j] = runningOrder[j - h]; + j = j - h; + if (j <= (h - 1)) + { + break; + } + } + runningOrder[j] = vv; + } + } while (h != 1); + + /*-- + The main sorting loop. + --*/ + for (i = 0; i <= 255; i++) + { + /*-- + Process big buckets, starting with the least full. + --*/ + ss = runningOrder[i]; + + /*-- + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j]. Hopefully + previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + --*/ + for (j = 0; j <= 255; j++) + { + sb = (ss << 8) + j; + if (!((ftab[sb] & SETMASK) == SETMASK)) + { + int lo = ftab[sb] & CLEARMASK; + int hi = (ftab[sb + 1] & CLEARMASK) - 1; + if (hi > lo) + { + QSort3(lo, hi, 2); + numQSorted += (hi - lo + 1); + if (workDone > workLimit && firstAttempt) + { + return; + } + } + ftab[sb] |= SETMASK; + } + } + + /*-- + The ss big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + --*/ + bigDone[ss] = true; + + if (i < 255) + { + int bbStart = ftab[ss << 8] & CLEARMASK; + int bbSize = (ftab[(ss + 1) << 8] & CLEARMASK) - bbStart; + int shifts = 0; + + while ((bbSize >> shifts) > 65534) + { + shifts++; + } + + for (j = 0; j < bbSize; j++) + { + int a2update = zptr[bbStart + j]; + int qVal = (j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZip2Constants.OvershootBytes) + { + quadrant[a2update + last + 1] = qVal; + } + } + + if (!(((bbSize - 1) >> shifts) <= 65535)) + { + Panic(); + } + } + + /*-- + Now scan this big bucket so as to synthesise the + sorted order for small buckets [t, ss] for all t != ss. + --*/ + for (j = 0; j <= 255; j++) + { + copy[j] = ftab[(j << 8) + ss] & CLEARMASK; + } + + for (j = ftab[ss << 8] & CLEARMASK; j < (ftab[(ss + 1) << 8] & CLEARMASK); j++) + { + c1 = block[zptr[j]]; + if (!bigDone[c1]) + { + zptr[copy[c1]] = zptr[j] == 0 ? last : zptr[j] - 1; + copy[c1]++; + } + } + + for (j = 0; j <= 255; j++) + { + ftab[(j << 8) + ss] |= SETMASK; + } + } + } + } + + private void RandomiseBlock() + { + int i; + int rNToGo = 0; + int rTPos = 0; + for (i = 0; i < 256; i++) + { + inUse[i] = false; + } + + for (i = 0; i <= last; i++) + { + if (rNToGo == 0) + { + rNToGo = (int)BZip2Constants.RandomNumbers[rTPos]; + rTPos++; + if (rTPos == 512) + { + rTPos = 0; + } + } + rNToGo--; + block[i + 1] ^= (byte)((rNToGo == 1) ? 1 : 0); + // handle 16 bit signed numbers + block[i + 1] &= 0xFF; + + inUse[block[i + 1]] = true; + } + } + + private void DoReversibleTransformation() + { + workLimit = workFactor * last; + workDone = 0; + blockRandomised = false; + firstAttempt = true; + + MainSort(); + + if (workDone > workLimit && firstAttempt) + { + RandomiseBlock(); + workLimit = workDone = 0; + blockRandomised = true; + firstAttempt = false; + MainSort(); + } + + origPtr = -1; + for (int i = 0; i <= last; i++) + { + if (zptr[i] == 0) + { + origPtr = i; + break; + } + } + + if (origPtr == -1) + { + Panic(); + } + } + + private bool FullGtU(int i1, int i2) + { + int k; + byte c1, c2; + int s1, s2; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return c1 > c2; + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return c1 > c2; + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return c1 > c2; + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return c1 > c2; + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return c1 > c2; + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return c1 > c2; + } + i1++; + i2++; + + k = last + 1; + + do + { + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return c1 > c2; + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) + { + return s1 > s2; + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return c1 > c2; + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) + { + return s1 > s2; + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return c1 > c2; + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) + { + return s1 > s2; + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return c1 > c2; + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) + { + return s1 > s2; + } + i1++; + i2++; + + if (i1 > last) + { + i1 -= last; + i1--; + } + if (i2 > last) + { + i2 -= last; + i2--; + } + + k -= 4; + ++workDone; + } while (k >= 0); + + return false; + } + + private void AllocateCompressStructures() + { + int n = BZip2Constants.BaseBlockSize * blockSize100k; + block = new byte[(n + 1 + BZip2Constants.OvershootBytes)]; + quadrant = new int[(n + BZip2Constants.OvershootBytes)]; + zptr = new int[n]; + ftab = new int[65537]; + + if (block == null || quadrant == null || zptr == null || ftab == null) + { + // int totalDraw = (n + 1 + NUM_OVERSHOOT_BYTES) + (n + NUM_OVERSHOOT_BYTES) + n + 65537; + // compressOutOfMemory ( totalDraw, n ); + } + + /* + The back end needs a place to store the MTF values + whilst it calculates the coding tables. We could + put them in the zptr array. However, these values + will fit in a short, so we overlay szptr at the + start of zptr, in the hope of reducing the number + of cache misses induced by the multiple traversals + of the MTF values when calculating coding tables. + Seems to improve compression speed by about 1%. + */ + // szptr = zptr; + + szptr = new short[2 * n]; + } + + private void GenerateMTFValues() + { + char[] yy = new char[256]; + int i, j; + char tmp; + char tmp2; + int zPend; + int wr; + int EOB; + + MakeMaps(); + EOB = nInUse + 1; + + for (i = 0; i <= EOB; i++) + { + mtfFreq[i] = 0; + } + + wr = 0; + zPend = 0; + for (i = 0; i < nInUse; i++) + { + yy[i] = (char)i; + } + + for (i = 0; i <= last; i++) + { + char ll_i; + + ll_i = unseqToSeq[block[zptr[i]]]; + + j = 0; + tmp = yy[j]; + while (ll_i != tmp) + { + j++; + tmp2 = tmp; + tmp = yy[j]; + yy[j] = tmp2; + } + yy[0] = tmp; + + if (j == 0) + { + zPend++; + } + else + { + if (zPend > 0) + { + zPend--; + while (true) + { + switch (zPend % 2) + { + case 0: + szptr[wr] = (short)BZip2Constants.RunA; + wr++; + mtfFreq[BZip2Constants.RunA]++; + break; + + case 1: + szptr[wr] = (short)BZip2Constants.RunB; + wr++; + mtfFreq[BZip2Constants.RunB]++; + break; + } + if (zPend < 2) + { + break; + } + zPend = (zPend - 2) / 2; + } + zPend = 0; + } + szptr[wr] = (short)(j + 1); + wr++; + mtfFreq[j + 1]++; + } + } + + if (zPend > 0) + { + zPend--; + while (true) + { + switch (zPend % 2) + { + case 0: + szptr[wr] = (short)BZip2Constants.RunA; + wr++; + mtfFreq[BZip2Constants.RunA]++; + break; + + case 1: + szptr[wr] = (short)BZip2Constants.RunB; + wr++; + mtfFreq[BZip2Constants.RunB]++; + break; + } + if (zPend < 2) + { + break; + } + zPend = (zPend - 2) / 2; + } + } + + szptr[wr] = (short)EOB; + wr++; + mtfFreq[EOB]++; + + nMTF = wr; + } + + private static void Panic() + { + throw new BZip2Exception("BZip2 output stream panic"); + } + + private static void HbMakeCodeLengths(char[] len, int[] freq, int alphaSize, int maxLen) + { + /*-- + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. + --*/ + int nNodes, nHeap, n1, n2, j, k; + bool tooLong; + + int[] heap = new int[BZip2Constants.MaximumAlphaSize + 2]; + int[] weight = new int[BZip2Constants.MaximumAlphaSize * 2]; + int[] parent = new int[BZip2Constants.MaximumAlphaSize * 2]; + + for (int i = 0; i < alphaSize; ++i) + { + weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + } + + while (true) + { + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (int i = 1; i <= alphaSize; ++i) + { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + int zz = nHeap; + int tmp = heap[zz]; + while (weight[tmp] < weight[heap[zz >> 1]]) + { + heap[zz] = heap[zz >> 1]; + zz >>= 1; + } + heap[zz] = tmp; + } + if (!(nHeap < (BZip2Constants.MaximumAlphaSize + 2))) + { + Panic(); + } + + while (nHeap > 1) + { + n1 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; + int zz = 1; + int yy = 0; + int tmp = heap[zz]; + while (true) + { + yy = zz << 1; + if (yy > nHeap) + { + break; + } + if (yy < nHeap && weight[heap[yy + 1]] < weight[heap[yy]]) + { + yy++; + } + if (weight[tmp] < weight[heap[yy]]) + { + break; + } + + heap[zz] = heap[yy]; + zz = yy; + } + heap[zz] = tmp; + n2 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; + + zz = 1; + yy = 0; + tmp = heap[zz]; + while (true) + { + yy = zz << 1; + if (yy > nHeap) + { + break; + } + if (yy < nHeap && weight[heap[yy + 1]] < weight[heap[yy]]) + { + yy++; + } + if (weight[tmp] < weight[heap[yy]]) + { + break; + } + heap[zz] = heap[yy]; + zz = yy; + } + heap[zz] = tmp; + nNodes++; + parent[n1] = parent[n2] = nNodes; + + weight[nNodes] = (int)((weight[n1] & 0xffffff00) + (weight[n2] & 0xffffff00)) | + (int)(1 + (((weight[n1] & 0x000000ff) > (weight[n2] & 0x000000ff)) ? (weight[n1] & 0x000000ff) : (weight[n2] & 0x000000ff))); + + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + + zz = nHeap; + tmp = heap[zz]; + while (weight[tmp] < weight[heap[zz >> 1]]) + { + heap[zz] = heap[zz >> 1]; + zz >>= 1; + } + heap[zz] = tmp; + } + if (!(nNodes < (BZip2Constants.MaximumAlphaSize * 2))) + { + Panic(); + } + + tooLong = false; + for (int i = 1; i <= alphaSize; ++i) + { + j = 0; + k = i; + while (parent[k] >= 0) + { + k = parent[k]; + j++; + } + len[i - 1] = (char)j; + tooLong |= j > maxLen; + } + + if (!tooLong) + { + break; + } + + for (int i = 1; i < alphaSize; ++i) + { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; + } + } + } + + private static void HbAssignCodes(int[] code, char[] length, int minLen, int maxLen, int alphaSize) + { + int vec = 0; + for (int n = minLen; n <= maxLen; ++n) + { + for (int i = 0; i < alphaSize; ++i) + { + if (length[i] == n) + { + code[i] = vec; + ++vec; + } + } + vec <<= 1; + } + } + + private static byte Med3(byte a, byte b, byte c) + { + byte t; + if (a > b) + { + t = a; + a = b; + b = t; + } + if (b > c) + { + t = b; + b = c; + c = t; + } + if (a > b) + { + b = a; + } + return b; + } + + private struct StackElement + { + public int ll; + public int hh; + public int dd; + } + } +} diff --git a/ICSharpCode.SharpZipLib/Checksum/Adler32.cs b/ICSharpCode.SharpZipLib/Checksum/Adler32.cs new file mode 100644 index 000000000000..b2a0f151a12c --- /dev/null +++ b/ICSharpCode.SharpZipLib/Checksum/Adler32.cs @@ -0,0 +1,163 @@ +using System; + +namespace ICSharpCode.SharpZipLib.Checksum +{ + /// + /// Computes Adler32 checksum for a stream of data. An Adler32 + /// checksum is not as reliable as a CRC32 checksum, but a lot faster to + /// compute. + /// + /// The specification for Adler32 may be found in RFC 1950. + /// ZLIB Compressed Data Format Specification version 3.3) + /// + /// + /// From that document: + /// + /// "ADLER32 (Adler-32 checksum) + /// This contains a checksum value of the uncompressed data + /// (excluding any dictionary data) computed according to Adler-32 + /// algorithm. This algorithm is a 32-bit extension and improvement + /// of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073 + /// standard. + /// + /// Adler-32 is composed of two sums accumulated per byte: s1 is + /// the sum of all bytes, s2 is the sum of all s1 values. Both sums + /// are done modulo 65521. s1 is initialized to 1, s2 to zero. The + /// Adler-32 checksum is stored as s2*65536 + s1 in most- + /// significant-byte first (network) order." + /// + /// "8.2. The Adler-32 algorithm + /// + /// The Adler-32 algorithm is much faster than the CRC32 algorithm yet + /// still provides an extremely low probability of undetected errors. + /// + /// The modulo on unsigned long accumulators can be delayed for 5552 + /// bytes, so the modulo operation time is negligible. If the bytes + /// are a, b, c, the second sum is 3a + 2b + c + 3, and so is position + /// and order sensitive, unlike the first sum, which is just a + /// checksum. That 65521 is prime is important to avoid a possible + /// large class of two-byte errors that leave the check unchanged. + /// (The Fletcher checksum uses 255, which is not prime and which also + /// makes the Fletcher check insensitive to single byte changes 0 - + /// 255.) + /// + /// The sum s1 is initialized to 1 instead of zero to make the length + /// of the sequence part of s2, so that the length does not have to be + /// checked separately. (Any sequence of zeroes has a Fletcher + /// checksum of zero.)" + /// + /// + /// + public sealed class Adler32 : IChecksum + { + #region Instance Fields + + /// + /// largest prime smaller than 65536 + /// + private static readonly uint BASE = 65521; + + /// + /// The CRC data checksum so far. + /// + private uint checkValue; + + #endregion Instance Fields + + /// + /// Initialise a default instance of + /// + public Adler32() + { + Reset(); + } + + /// + /// Resets the Adler32 data checksum as if no update was ever called. + /// + public void Reset() + { + checkValue = 1; + } + + /// + /// Returns the Adler32 data checksum computed so far. + /// + public long Value + { + get + { + return checkValue; + } + } + + /// + /// Updates the checksum with the byte b. + /// + /// + /// The data value to add. The high byte of the int is ignored. + /// + public void Update(int bval) + { + // We could make a length 1 byte array and call update again, but I + // would rather not have that overhead + uint s1 = checkValue & 0xFFFF; + uint s2 = checkValue >> 16; + + s1 = (s1 + ((uint)bval & 0xFF)) % BASE; + s2 = (s1 + s2) % BASE; + + checkValue = (s2 << 16) + s1; + } + + /// + /// Updates the Adler32 data checksum with the bytes taken from + /// a block of data. + /// + /// Contains the data to update the checksum with. + public void Update(byte[] buffer) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + Update(new ArraySegment(buffer, 0, buffer.Length)); + } + + /// + /// Update Adler32 data checksum based on a portion of a block of data + /// + /// + /// The chunk of data to add + /// + public void Update(ArraySegment segment) + { + //(By Per Bothner) + uint s1 = checkValue & 0xFFFF; + uint s2 = checkValue >> 16; + var count = segment.Count; + var offset = segment.Offset; + while (count > 0) + { + // We can defer the modulo operation: + // s1 maximally grows from 65521 to 65521 + 255 * 3800 + // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31 + int n = 3800; + if (n > count) + { + n = count; + } + count -= n; + while (--n >= 0) + { + s1 = s1 + (uint)(segment.Array[offset++] & 0xff); + s2 = s2 + s1; + } + s1 %= BASE; + s2 %= BASE; + } + checkValue = (s2 << 16) | s1; + } + } +} diff --git a/ICSharpCode.SharpZipLib/Checksum/BZip2Crc.cs b/ICSharpCode.SharpZipLib/Checksum/BZip2Crc.cs new file mode 100644 index 000000000000..be76da11eb16 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Checksum/BZip2Crc.cs @@ -0,0 +1,171 @@ +using System; +using System.Runtime.CompilerServices; + +namespace ICSharpCode.SharpZipLib.Checksum +{ + /// + /// CRC-32 with unreversed data and reversed output + /// + /// + /// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: + /// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0. + /// + /// Polynomials over GF(2) are represented in binary, one bit per coefficient, + /// with the lowest powers in the most significant bit. Then adding polynomials + /// is just exclusive-or, and multiplying a polynomial by x is a right shift by + /// one. If we call the above polynomial p, and represent a byte as the + /// polynomial q, also with the lowest power in the most significant bit (so the + /// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + /// where a mod b means the remainder after dividing a by b. + /// + /// This calculation is done using the shift-register method of multiplying and + /// taking the remainder. The register is initialized to zero, and for each + /// incoming bit, x^32 is added mod p to the register if the bit is a one (where + /// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + /// x (which is shifting right by one and adding x^32 mod p if the bit shifted + /// out is a one). We start with the highest power (least significant bit) of + /// q and repeat for all eight bits of q. + /// + /// This implementation uses sixteen lookup tables stored in one linear array + /// to implement the slicing-by-16 algorithm, a variant of the slicing-by-8 + /// algorithm described in this Intel white paper: + /// + /// https://web.archive.org/web/20120722193753/http://download.intel.com/technology/comms/perfnet/download/slicing-by-8.pdf + /// + /// The first lookup table is simply the CRC of all possible eight bit values. + /// Each successive lookup table is derived from the original table generated + /// by Sarwate's algorithm. Slicing a 16-bit input and XORing the outputs + /// together will produce the same output as a byte-by-byte CRC loop with + /// fewer arithmetic and bit manipulation operations, at the cost of increased + /// memory consumed by the lookup tables. (Slicing-by-16 requires a 16KB table, + /// which is still small enough to fit in most processors' L1 cache.) + /// + public sealed class BZip2Crc : IChecksum + { + #region Instance Fields + + private const uint crcInit = 0xFFFFFFFF; + //const uint crcXor = 0x00000000; + + private static readonly uint[] crcTable = CrcUtilities.GenerateSlicingLookupTable(0x04C11DB7, isReversed: false); + + /// + /// The CRC data checksum so far. + /// + private uint checkValue; + + #endregion Instance Fields + + /// + /// Initialise a default instance of + /// + public BZip2Crc() + { + Reset(); + } + + /// + /// Resets the CRC data checksum as if no update was ever called. + /// + public void Reset() + { + checkValue = crcInit; + } + + /// + /// Returns the CRC data checksum computed so far. + /// + /// Reversed Out = true + public long Value + { + get + { + // Technically, the output should be: + //return (long)(~checkValue ^ crcXor); + // but x ^ 0 = x, so there is no point in adding + // the XOR operation + return (long)(~checkValue); + } + } + + /// + /// Updates the checksum with the int bval. + /// + /// + /// the byte is taken as the lower 8 bits of bval + /// + /// Reversed Data = false + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Update(int bval) + { + checkValue = unchecked(crcTable[(byte)(((checkValue >> 24) & 0xFF) ^ bval)] ^ (checkValue << 8)); + } + + /// + /// Updates the CRC data checksum with the bytes taken from + /// a block of data. + /// + /// Contains the data to update the CRC with. + public void Update(byte[] buffer) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + Update(buffer, 0, buffer.Length); + } + + /// + /// Update CRC data checksum based on a portion of a block of data + /// + /// + /// The chunk of data to add + /// + public void Update(ArraySegment segment) + { + Update(segment.Array, segment.Offset, segment.Count); + } + + /// + /// Internal helper function for updating a block of data using slicing. + /// + /// The array containing the data to add + /// Range start for (inclusive) + /// The number of bytes to checksum starting from + private void Update(byte[] data, int offset, int count) + { + int remainder = count % CrcUtilities.SlicingDegree; + int end = offset + count - remainder; + + while (offset != end) + { + checkValue = CrcUtilities.UpdateDataForNormalPoly(data, offset, crcTable, checkValue); + offset += CrcUtilities.SlicingDegree; + } + + if (remainder != 0) + { + SlowUpdateLoop(data, offset, end + remainder); + } + } + + /// + /// A non-inlined function for updating data that doesn't fit in a 16-byte + /// block. We don't expect to enter this function most of the time, and when + /// we do we're not here for long, so disabling inlining here improves + /// performance overall. + /// + /// The array containing the data to add + /// Range start for (inclusive) + /// Range end for (exclusive) + [MethodImpl(MethodImplOptions.NoInlining)] + private void SlowUpdateLoop(byte[] data, int offset, int end) + { + while (offset != end) + { + Update(data[offset++]); + } + } + } +} diff --git a/ICSharpCode.SharpZipLib/Checksum/Crc32.cs b/ICSharpCode.SharpZipLib/Checksum/Crc32.cs new file mode 100644 index 000000000000..740aff566835 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Checksum/Crc32.cs @@ -0,0 +1,173 @@ +using System; +using System.Runtime.CompilerServices; + +namespace ICSharpCode.SharpZipLib.Checksum +{ + /// + /// CRC-32 with reversed data and unreversed output + /// + /// + /// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: + /// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0. + /// + /// Polynomials over GF(2) are represented in binary, one bit per coefficient, + /// with the lowest powers in the most significant bit. Then adding polynomials + /// is just exclusive-or, and multiplying a polynomial by x is a right shift by + /// one. If we call the above polynomial p, and represent a byte as the + /// polynomial q, also with the lowest power in the most significant bit (so the + /// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + /// where a mod b means the remainder after dividing a by b. + /// + /// This calculation is done using the shift-register method of multiplying and + /// taking the remainder. The register is initialized to zero, and for each + /// incoming bit, x^32 is added mod p to the register if the bit is a one (where + /// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + /// x (which is shifting right by one and adding x^32 mod p if the bit shifted + /// out is a one). We start with the highest power (least significant bit) of + /// q and repeat for all eight bits of q. + /// + /// This implementation uses sixteen lookup tables stored in one linear array + /// to implement the slicing-by-16 algorithm, a variant of the slicing-by-8 + /// algorithm described in this Intel white paper: + /// + /// https://web.archive.org/web/20120722193753/http://download.intel.com/technology/comms/perfnet/download/slicing-by-8.pdf + /// + /// The first lookup table is simply the CRC of all possible eight bit values. + /// Each successive lookup table is derived from the original table generated + /// by Sarwate's algorithm. Slicing a 16-bit input and XORing the outputs + /// together will produce the same output as a byte-by-byte CRC loop with + /// fewer arithmetic and bit manipulation operations, at the cost of increased + /// memory consumed by the lookup tables. (Slicing-by-16 requires a 16KB table, + /// which is still small enough to fit in most processors' L1 cache.) + /// + public sealed class Crc32 : IChecksum + { + #region Instance Fields + + private static readonly uint crcInit = 0xFFFFFFFF; + private static readonly uint crcXor = 0xFFFFFFFF; + + private static readonly uint[] crcTable = CrcUtilities.GenerateSlicingLookupTable(0xEDB88320, isReversed: true); + + /// + /// The CRC data checksum so far. + /// + private uint checkValue; + + #endregion Instance Fields + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static uint ComputeCrc32(uint oldCrc, byte bval) + { + return (uint)(Crc32.crcTable[(oldCrc ^ bval) & 0xFF] ^ (oldCrc >> 8)); + } + + /// + /// Initialise a default instance of + /// + public Crc32() + { + Reset(); + } + + /// + /// Resets the CRC data checksum as if no update was ever called. + /// + public void Reset() + { + checkValue = crcInit; + } + + /// + /// Returns the CRC data checksum computed so far. + /// + /// Reversed Out = false + public long Value + { + get + { + return (long)(checkValue ^ crcXor); + } + } + + /// + /// Updates the checksum with the int bval. + /// + /// + /// the byte is taken as the lower 8 bits of bval + /// + /// Reversed Data = true + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Update(int bval) + { + checkValue = unchecked(crcTable[(checkValue ^ bval) & 0xFF] ^ (checkValue >> 8)); + } + + /// + /// Updates the CRC data checksum with the bytes taken from + /// a block of data. + /// + /// Contains the data to update the CRC with. + public void Update(byte[] buffer) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + Update(buffer, 0, buffer.Length); + } + + /// + /// Update CRC data checksum based on a portion of a block of data + /// + /// + /// The chunk of data to add + /// + public void Update(ArraySegment segment) + { + Update(segment.Array, segment.Offset, segment.Count); + } + + /// + /// Internal helper function for updating a block of data using slicing. + /// + /// The array containing the data to add + /// Range start for (inclusive) + /// The number of bytes to checksum starting from + private void Update(byte[] data, int offset, int count) + { + int remainder = count % CrcUtilities.SlicingDegree; + int end = offset + count - remainder; + + while (offset != end) + { + checkValue = CrcUtilities.UpdateDataForReversedPoly(data, offset, crcTable, checkValue); + offset += CrcUtilities.SlicingDegree; + } + + if (remainder != 0) + { + SlowUpdateLoop(data, offset, end + remainder); + } + } + + /// + /// A non-inlined function for updating data that doesn't fit in a 16-byte + /// block. We don't expect to enter this function most of the time, and when + /// we do we're not here for long, so disabling inlining here improves + /// performance overall. + /// + /// The array containing the data to add + /// Range start for (inclusive) + /// Range end for (exclusive) + [MethodImpl(MethodImplOptions.NoInlining)] + private void SlowUpdateLoop(byte[] data, int offset, int end) + { + while (offset != end) + { + Update(data[offset++]); + } + } + } +} diff --git a/ICSharpCode.SharpZipLib/Checksum/CrcUtilities.cs b/ICSharpCode.SharpZipLib/Checksum/CrcUtilities.cs new file mode 100644 index 000000000000..575abe08b5c3 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Checksum/CrcUtilities.cs @@ -0,0 +1,158 @@ +using System.Runtime.CompilerServices; + +namespace ICSharpCode.SharpZipLib.Checksum +{ + internal static class CrcUtilities + { + /// + /// The number of slicing lookup tables to generate. + /// + internal const int SlicingDegree = 16; + + /// + /// Generates multiple CRC lookup tables for a given polynomial, stored + /// in a linear array of uints. The first block (i.e. the first 256 + /// elements) is the same as the byte-by-byte CRC lookup table. + /// + /// The generating CRC polynomial + /// Whether the polynomial is in reversed bit order + /// A linear array of 256 * elements + /// + /// This table could also be generated as a rectangular array, but the + /// JIT compiler generates slower code than if we use a linear array. + /// Known issue, see: https://github.com/dotnet/runtime/issues/30275 + /// + internal static uint[] GenerateSlicingLookupTable(uint polynomial, bool isReversed) + { + var table = new uint[256 * SlicingDegree]; + uint one = isReversed ? 1 : (1U << 31); + + for (int i = 0; i < 256; i++) + { + uint res = (uint)(isReversed ? i : i << 24); + for (int j = 0; j < SlicingDegree; j++) + { + for (int k = 0; k < 8; k++) + { + if (isReversed) + { + res = (res & one) == 1 ? polynomial ^ (res >> 1) : res >> 1; + } + else + { + res = (res & one) != 0 ? polynomial ^ (res << 1) : res << 1; + } + } + + table[(256 * j) + i] = res; + } + } + + return table; + } + + /// + /// Mixes the first four bytes of input with + /// using normal ordering before calling . + /// + /// Array of data to checksum + /// Offset to start reading from + /// The table to use for slicing-by-16 lookup + /// Checksum state before this update call + /// A new unfinalized checksum value + /// + /// + /// Assumes input[offset]..input[offset + 15] are valid array indexes. + /// For performance reasons, this must be checked by the caller. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static uint UpdateDataForNormalPoly(byte[] input, int offset, uint[] crcTable, uint checkValue) + { + byte x1 = (byte)((byte)(checkValue >> 24) ^ input[offset]); + byte x2 = (byte)((byte)(checkValue >> 16) ^ input[offset + 1]); + byte x3 = (byte)((byte)(checkValue >> 8) ^ input[offset + 2]); + byte x4 = (byte)((byte)checkValue ^ input[offset + 3]); + + return UpdateDataCommon(input, offset, crcTable, x1, x2, x3, x4); + } + + /// + /// Mixes the first four bytes of input with + /// using reflected ordering before calling . + /// + /// Array of data to checksum + /// Offset to start reading from + /// The table to use for slicing-by-16 lookup + /// Checksum state before this update call + /// A new unfinalized checksum value + /// + /// + /// Assumes input[offset]..input[offset + 15] are valid array indexes. + /// For performance reasons, this must be checked by the caller. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static uint UpdateDataForReversedPoly(byte[] input, int offset, uint[] crcTable, uint checkValue) + { + byte x1 = (byte)((byte)checkValue ^ input[offset]); + byte x2 = (byte)((byte)(checkValue >>= 8) ^ input[offset + 1]); + byte x3 = (byte)((byte)(checkValue >>= 8) ^ input[offset + 2]); + byte x4 = (byte)((byte)(checkValue >>= 8) ^ input[offset + 3]); + + return UpdateDataCommon(input, offset, crcTable, x1, x2, x3, x4); + } + + /// + /// A shared method for updating an unfinalized CRC checksum using slicing-by-16. + /// + /// Array of data to checksum + /// Offset to start reading from + /// The table to use for slicing-by-16 lookup + /// First byte of input after mixing with the old CRC + /// Second byte of input after mixing with the old CRC + /// Third byte of input after mixing with the old CRC + /// Fourth byte of input after mixing with the old CRC + /// A new unfinalized checksum value + /// + /// + /// Even though the first four bytes of input are fed in as arguments, + /// should be the same value passed to this + /// function's caller (either or + /// ). This method will get inlined + /// into both functions, so using the same offset produces faster code. + /// + /// + /// Because most processors running C# have some kind of instruction-level + /// parallelism, the order of XOR operations can affect performance. This + /// ordering assumes that the assembly code generated by the just-in-time + /// compiler will emit a bunch of arithmetic operations for checking array + /// bounds. Then it opportunistically XORs a1 and a2 to keep the processor + /// busy while those other parts of the pipeline handle the range check + /// calculations. + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint UpdateDataCommon(byte[] input, int offset, uint[] crcTable, byte x1, byte x2, byte x3, byte x4) + { + uint result; + uint a1 = crcTable[x1 + 3840] ^ crcTable[x2 + 3584]; + uint a2 = crcTable[x3 + 3328] ^ crcTable[x4 + 3072]; + + result = crcTable[input[offset + 4] + 2816]; + result ^= crcTable[input[offset + 5] + 2560]; + a1 ^= crcTable[input[offset + 9] + 1536]; + result ^= crcTable[input[offset + 6] + 2304]; + result ^= crcTable[input[offset + 7] + 2048]; + result ^= crcTable[input[offset + 8] + 1792]; + a2 ^= crcTable[input[offset + 13] + 512]; + result ^= crcTable[input[offset + 10] + 1280]; + result ^= crcTable[input[offset + 11] + 1024]; + result ^= crcTable[input[offset + 12] + 768]; + result ^= a1; + result ^= crcTable[input[offset + 14] + 256]; + result ^= crcTable[input[offset + 15]]; + result ^= a2; + + return result; + } + } +} diff --git a/ICSharpCode.SharpZipLib/Checksum/IChecksum.cs b/ICSharpCode.SharpZipLib/Checksum/IChecksum.cs new file mode 100644 index 000000000000..db74a5a5d20a --- /dev/null +++ b/ICSharpCode.SharpZipLib/Checksum/IChecksum.cs @@ -0,0 +1,51 @@ +using System; + +namespace ICSharpCode.SharpZipLib.Checksum +{ + /// + /// Interface to compute a data checksum used by checked input/output streams. + /// A data checksum can be updated by one byte or with a byte array. After each + /// update the value of the current checksum can be returned by calling + /// getValue. The complete checksum object can also be reset + /// so it can be used again with new data. + /// + public interface IChecksum + { + /// + /// Resets the data checksum as if no update was ever called. + /// + void Reset(); + + /// + /// Returns the data checksum computed so far. + /// + long Value + { + get; + } + + /// + /// Adds one byte to the data checksum. + /// + /// + /// the data value to add. The high byte of the int is ignored. + /// + void Update(int bval); + + /// + /// Updates the data checksum with the bytes taken from the array. + /// + /// + /// buffer an array of bytes + /// + void Update(byte[] buffer); + + /// + /// Adds the byte array to the data checksum. + /// + /// + /// The chunk of data to add + /// + void Update(ArraySegment segment); + } +} diff --git a/ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs b/ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs new file mode 100644 index 000000000000..a2e30da7f2ee --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs @@ -0,0 +1,130 @@ +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using CT = System.Threading.CancellationToken; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable InconsistentNaming + +namespace ICSharpCode.SharpZipLib.Core +{ + internal static class ByteOrderStreamExtensions + { + internal static byte[] SwappedBytes(ushort value) => new[] {(byte)value, (byte)(value >> 8)}; + internal static byte[] SwappedBytes(short value) => new[] {(byte)value, (byte)(value >> 8)}; + internal static byte[] SwappedBytes(uint value) => new[] {(byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24)}; + internal static byte[] SwappedBytes(int value) => new[] {(byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24)}; + + internal static byte[] SwappedBytes(long value) => new[] { + (byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24), + (byte)(value >> 32), (byte)(value >> 40), (byte)(value >> 48), (byte)(value >> 56) + }; + + internal static byte[] SwappedBytes(ulong value) => new[] { + (byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24), + (byte)(value >> 32), (byte)(value >> 40), (byte)(value >> 48), (byte)(value >> 56) + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static long SwappedS64(byte[] bytes) => ( + (long)bytes[0] << 0 | (long)bytes[1] << 8 | (long)bytes[2] << 16 | (long)bytes[3] << 24 | + (long)bytes[4] << 32 | (long)bytes[5] << 40 | (long)bytes[6] << 48 | (long)bytes[7] << 56); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ulong SwappedU64(byte[] bytes) => ( + (ulong)bytes[0] << 0 | (ulong)bytes[1] << 8 | (ulong)bytes[2] << 16 | (ulong)bytes[3] << 24 | + (ulong)bytes[4] << 32 | (ulong)bytes[5] << 40 | (ulong)bytes[6] << 48 | (ulong)bytes[7] << 56); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int SwappedS32(byte[] bytes) => bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static uint SwappedU32(byte[] bytes) => (uint) SwappedS32(bytes); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static short SwappedS16(byte[] bytes) => (short)(bytes[0] | bytes[1] << 8); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ushort SwappedU16(byte[] bytes) => (ushort) SwappedS16(bytes); + + internal static byte[] ReadBytes(this Stream stream, int count) + { + var bytes = new byte[count]; + var remaining = count; + while (remaining > 0) + { + var bytesRead = stream.Read(bytes, count - remaining, remaining); + if (bytesRead < 1) throw new EndOfStreamException(); + remaining -= bytesRead; + } + + return bytes; + } + + /// Read an unsigned short in little endian byte order. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ReadLEShort(this Stream stream) => SwappedS16(ReadBytes(stream, 2)); + + /// Read an int in little endian byte order. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ReadLEInt(this Stream stream) => SwappedS32(ReadBytes(stream, 4)); + + /// Read a long in little endian byte order. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long ReadLELong(this Stream stream) => SwappedS64(ReadBytes(stream, 8)); + + /// Write an unsigned short in little endian byte order. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteLEShort(this Stream stream, int value) => stream.Write(SwappedBytes(value), 0, 2); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static async Task WriteLEShortAsync(this Stream stream, int value, CT ct) + => await stream.WriteAsync(SwappedBytes(value), 0, 2, ct); + + /// Write a ushort in little endian byte order. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteLEUshort(this Stream stream, ushort value) => stream.Write(SwappedBytes(value), 0, 2); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static async Task WriteLEUshortAsync(this Stream stream, ushort value, CT ct) + => await stream.WriteAsync(SwappedBytes(value), 0, 2, ct); + + /// Write an int in little endian byte order. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteLEInt(this Stream stream, int value) => stream.Write(SwappedBytes(value), 0, 4); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static async Task WriteLEIntAsync(this Stream stream, int value, CT ct) + => await stream.WriteAsync(SwappedBytes(value), 0, 4, ct); + + /// Write a uint in little endian byte order. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteLEUint(this Stream stream, uint value) => stream.Write(SwappedBytes(value), 0, 4); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static async Task WriteLEUintAsync(this Stream stream, uint value, CT ct) + => await stream.WriteAsync(SwappedBytes(value), 0, 4, ct); + + /// Write a long in little endian byte order. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteLELong(this Stream stream, long value) => stream.Write(SwappedBytes(value), 0, 8); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static async Task WriteLELongAsync(this Stream stream, long value, CT ct) + => await stream.WriteAsync(SwappedBytes(value), 0, 8, ct); + + /// Write a ulong in little endian byte order. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteLEUlong(this Stream stream, ulong value) => stream.Write(SwappedBytes(value), 0, 8); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static async Task WriteLEUlongAsync(this Stream stream, ulong value, CT ct) + => await stream.WriteAsync(SwappedBytes(value), 0, 8, ct); + } +} diff --git a/ICSharpCode.SharpZipLib/Core/EmptyRefs.cs b/ICSharpCode.SharpZipLib/Core/EmptyRefs.cs new file mode 100644 index 000000000000..feb7a8e17cb9 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/EmptyRefs.cs @@ -0,0 +1,17 @@ +using System; + +namespace ICSharpCode.SharpZipLib.Core +{ + internal static class Empty + { +#if NET45 + internal static class EmptyArray + { + public static readonly T[] Value = new T[0]; + } + public static T[] Array() => EmptyArray.Value; +#else + public static T[] Array() => System.Array.Empty(); +#endif + } +} diff --git a/ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs b/ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs new file mode 100644 index 000000000000..eb14e2d49f9a --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs @@ -0,0 +1,58 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib +{ + /// + /// SharpZipBaseException is the base exception class for SharpZipLib. + /// All library exceptions are derived from this. + /// + /// NOTE: Not all exceptions thrown will be derived from this class. + /// A variety of other exceptions are possible for example + [Serializable] + public class SharpZipBaseException : Exception + { + /// + /// Initializes a new instance of the SharpZipBaseException class. + /// + public SharpZipBaseException() + { + } + + /// + /// Initializes a new instance of the SharpZipBaseException class with a specified error message. + /// + /// A message describing the exception. + public SharpZipBaseException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the SharpZipBaseException class with a specified + /// error message and a reference to the inner exception that is the cause of this exception. + /// + /// A message describing the exception. + /// The inner exception + public SharpZipBaseException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the SharpZipBaseException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected SharpZipBaseException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs b/ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs new file mode 100644 index 000000000000..e22b11b0d164 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs @@ -0,0 +1,50 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib +{ + /// + /// Indicates that an error occurred during decoding of a input stream due to corrupt + /// data or (unintentional) library incompatibility. + /// + [Serializable] + public class StreamDecodingException : SharpZipBaseException + { + private const string GenericMessage = "Input stream could not be decoded"; + + /// + /// Initializes a new instance of the StreamDecodingException with a generic message + /// + public StreamDecodingException() : base(GenericMessage) { } + + /// + /// Initializes a new instance of the StreamDecodingException class with a specified error message. + /// + /// A message describing the exception. + public StreamDecodingException(string message) : base(message) { } + + /// + /// Initializes a new instance of the StreamDecodingException class with a specified + /// error message and a reference to the inner exception that is the cause of this exception. + /// + /// A message describing the exception. + /// The inner exception + public StreamDecodingException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the StreamDecodingException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected StreamDecodingException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs b/ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs new file mode 100644 index 000000000000..5827e559d0b1 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib +{ + /// + /// Indicates that the input stream could not decoded due to known library incompability or missing features + /// + [Serializable] + public class StreamUnsupportedException : StreamDecodingException + { + private const string GenericMessage = "Input stream is in a unsupported format"; + + /// + /// Initializes a new instance of the StreamUnsupportedException with a generic message + /// + public StreamUnsupportedException() : base(GenericMessage) { } + + /// + /// Initializes a new instance of the StreamUnsupportedException class with a specified error message. + /// + /// A message describing the exception. + public StreamUnsupportedException(string message) : base(message) { } + + /// + /// Initializes a new instance of the StreamUnsupportedException class with a specified + /// error message and a reference to the inner exception that is the cause of this exception. + /// + /// A message describing the exception. + /// The inner exception + public StreamUnsupportedException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the StreamUnsupportedException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected StreamUnsupportedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs b/ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs new file mode 100644 index 000000000000..a35c49f0379e --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib +{ + /// + /// Indicates that the input stream could not decoded due to the stream ending before enough data had been provided + /// + [Serializable] + public class UnexpectedEndOfStreamException : StreamDecodingException + { + private const string GenericMessage = "Input stream ended unexpectedly"; + + /// + /// Initializes a new instance of the UnexpectedEndOfStreamException with a generic message + /// + public UnexpectedEndOfStreamException() : base(GenericMessage) { } + + /// + /// Initializes a new instance of the UnexpectedEndOfStreamException class with a specified error message. + /// + /// A message describing the exception. + public UnexpectedEndOfStreamException(string message) : base(message) { } + + /// + /// Initializes a new instance of the UnexpectedEndOfStreamException class with a specified + /// error message and a reference to the inner exception that is the cause of this exception. + /// + /// A message describing the exception. + /// The inner exception + public UnexpectedEndOfStreamException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the UnexpectedEndOfStreamException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected UnexpectedEndOfStreamException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs b/ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs new file mode 100644 index 000000000000..d41cf9882750 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib +{ + /// + /// Indicates that a value was outside of the expected range when decoding an input stream + /// + [Serializable] + public class ValueOutOfRangeException : StreamDecodingException + { + /// + /// Initializes a new instance of the ValueOutOfRangeException class naming the causing variable + /// + /// Name of the variable, use: nameof() + public ValueOutOfRangeException(string nameOfValue) + : base($"{nameOfValue} out of range") { } + + /// + /// Initializes a new instance of the ValueOutOfRangeException class naming the causing variable, + /// it's current value and expected range. + /// + /// Name of the variable, use: nameof() + /// The invalid value + /// Expected maximum value + /// Expected minimum value + public ValueOutOfRangeException(string nameOfValue, long value, long maxValue, long minValue = 0) + : this(nameOfValue, value.ToString(), maxValue.ToString(), minValue.ToString()) { } + + /// + /// Initializes a new instance of the ValueOutOfRangeException class naming the causing variable, + /// it's current value and expected range. + /// + /// Name of the variable, use: nameof() + /// The invalid value + /// Expected maximum value + /// Expected minimum value + public ValueOutOfRangeException(string nameOfValue, string value, string maxValue, string minValue = "0") : + base($"{nameOfValue} out of range: {value}, should be {minValue}..{maxValue}") + { } + + private ValueOutOfRangeException() + { + } + + private ValueOutOfRangeException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the ValueOutOfRangeException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected ValueOutOfRangeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Core/FileSystemScanner.cs b/ICSharpCode.SharpZipLib/Core/FileSystemScanner.cs new file mode 100644 index 000000000000..427e7d895c3b --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/FileSystemScanner.cs @@ -0,0 +1,545 @@ +using System; + +namespace ICSharpCode.SharpZipLib.Core +{ + #region EventArgs + + /// + /// Event arguments for scanning. + /// + public class ScanEventArgs : EventArgs + { + #region Constructors + + /// + /// Initialise a new instance of + /// + /// The file or directory name. + public ScanEventArgs(string name) + { + name_ = name; + } + + #endregion Constructors + + /// + /// The file or directory name for this event. + /// + public string Name + { + get { return name_; } + } + + /// + /// Get set a value indicating if scanning should continue or not. + /// + public bool ContinueRunning + { + get { return continueRunning_; } + set { continueRunning_ = value; } + } + + #region Instance Fields + + private string name_; + private bool continueRunning_ = true; + + #endregion Instance Fields + } + + /// + /// Event arguments during processing of a single file or directory. + /// + public class ProgressEventArgs : EventArgs + { + #region Constructors + + /// + /// Initialise a new instance of + /// + /// The file or directory name if known. + /// The number of bytes processed so far + /// The total number of bytes to process, 0 if not known + public ProgressEventArgs(string name, long processed, long target) + { + name_ = name; + processed_ = processed; + target_ = target; + } + + #endregion Constructors + + /// + /// The name for this event if known. + /// + public string Name + { + get { return name_; } + } + + /// + /// Get set a value indicating whether scanning should continue or not. + /// + public bool ContinueRunning + { + get { return continueRunning_; } + set { continueRunning_ = value; } + } + + /// + /// Get a percentage representing how much of the has been processed + /// + /// 0.0 to 100.0 percent; 0 if target is not known. + public float PercentComplete + { + get + { + float result; + if (target_ <= 0) + { + result = 0; + } + else + { + result = ((float)processed_ / (float)target_) * 100.0f; + } + return result; + } + } + + /// + /// The number of bytes processed so far + /// + public long Processed + { + get { return processed_; } + } + + /// + /// The number of bytes to process. + /// + /// Target may be 0 or negative if the value isnt known. + public long Target + { + get { return target_; } + } + + #region Instance Fields + + private string name_; + private long processed_; + private long target_; + private bool continueRunning_ = true; + + #endregion Instance Fields + } + + /// + /// Event arguments for directories. + /// + public class DirectoryEventArgs : ScanEventArgs + { + #region Constructors + + /// + /// Initialize an instance of . + /// + /// The name for this directory. + /// Flag value indicating if any matching files are contained in this directory. + public DirectoryEventArgs(string name, bool hasMatchingFiles) + : base(name) + { + hasMatchingFiles_ = hasMatchingFiles; + } + + #endregion Constructors + + /// + /// Get a value indicating if the directory contains any matching files or not. + /// + public bool HasMatchingFiles + { + get { return hasMatchingFiles_; } + } + + private readonly + + #region Instance Fields + + bool hasMatchingFiles_; + + #endregion Instance Fields + } + + /// + /// Arguments passed when scan failures are detected. + /// + public class ScanFailureEventArgs : EventArgs + { + #region Constructors + + /// + /// Initialise a new instance of + /// + /// The name to apply. + /// The exception to use. + public ScanFailureEventArgs(string name, Exception e) + { + name_ = name; + exception_ = e; + continueRunning_ = true; + } + + #endregion Constructors + + /// + /// The applicable name. + /// + public string Name + { + get { return name_; } + } + + /// + /// The applicable exception. + /// + public Exception Exception + { + get { return exception_; } + } + + /// + /// Get / set a value indicating whether scanning should continue. + /// + public bool ContinueRunning + { + get { return continueRunning_; } + set { continueRunning_ = value; } + } + + #region Instance Fields + + private string name_; + private Exception exception_; + private bool continueRunning_; + + #endregion Instance Fields + } + + #endregion EventArgs + + #region Delegates + + /// + /// Delegate invoked before starting to process a file. + /// + /// The source of the event + /// The event arguments. + public delegate void ProcessFileHandler(object sender, ScanEventArgs e); + + /// + /// Delegate invoked during processing of a file or directory + /// + /// The source of the event + /// The event arguments. + public delegate void ProgressHandler(object sender, ProgressEventArgs e); + + /// + /// Delegate invoked when a file has been completely processed. + /// + /// The source of the event + /// The event arguments. + public delegate void CompletedFileHandler(object sender, ScanEventArgs e); + + /// + /// Delegate invoked when a directory failure is detected. + /// + /// The source of the event + /// The event arguments. + public delegate void DirectoryFailureHandler(object sender, ScanFailureEventArgs e); + + /// + /// Delegate invoked when a file failure is detected. + /// + /// The source of the event + /// The event arguments. + public delegate void FileFailureHandler(object sender, ScanFailureEventArgs e); + + #endregion Delegates + + /// + /// FileSystemScanner provides facilities scanning of files and directories. + /// + public class FileSystemScanner + { + #region Constructors + + /// + /// Initialise a new instance of + /// + /// The file filter to apply when scanning. + public FileSystemScanner(string filter) + { + fileFilter_ = new PathFilter(filter); + } + + /// + /// Initialise a new instance of + /// + /// The file filter to apply. + /// The directory filter to apply. + public FileSystemScanner(string fileFilter, string directoryFilter) + { + fileFilter_ = new PathFilter(fileFilter); + directoryFilter_ = new PathFilter(directoryFilter); + } + + /// + /// Initialise a new instance of + /// + /// The file filter to apply. + public FileSystemScanner(IScanFilter fileFilter) + { + fileFilter_ = fileFilter; + } + + /// + /// Initialise a new instance of + /// + /// The file filter to apply. + /// The directory filter to apply. + public FileSystemScanner(IScanFilter fileFilter, IScanFilter directoryFilter) + { + fileFilter_ = fileFilter; + directoryFilter_ = directoryFilter; + } + + #endregion Constructors + + #region Delegates + + /// + /// Delegate to invoke when a directory is processed. + /// + public event EventHandler ProcessDirectory; + + /// + /// Delegate to invoke when a file is processed. + /// + public ProcessFileHandler ProcessFile; + + /// + /// Delegate to invoke when processing for a file has finished. + /// + public CompletedFileHandler CompletedFile; + + /// + /// Delegate to invoke when a directory failure is detected. + /// + public DirectoryFailureHandler DirectoryFailure; + + /// + /// Delegate to invoke when a file failure is detected. + /// + public FileFailureHandler FileFailure; + + #endregion Delegates + + /// + /// Raise the DirectoryFailure event. + /// + /// The directory name. + /// The exception detected. + private bool OnDirectoryFailure(string directory, Exception e) + { + DirectoryFailureHandler handler = DirectoryFailure; + bool result = (handler != null); + if (result) + { + var args = new ScanFailureEventArgs(directory, e); + handler(this, args); + alive_ = args.ContinueRunning; + } + return result; + } + + /// + /// Raise the FileFailure event. + /// + /// The file name. + /// The exception detected. + private bool OnFileFailure(string file, Exception e) + { + FileFailureHandler handler = FileFailure; + + bool result = (handler != null); + + if (result) + { + var args = new ScanFailureEventArgs(file, e); + FileFailure(this, args); + alive_ = args.ContinueRunning; + } + return result; + } + + /// + /// Raise the ProcessFile event. + /// + /// The file name. + private void OnProcessFile(string file) + { + ProcessFileHandler handler = ProcessFile; + + if (handler != null) + { + var args = new ScanEventArgs(file); + handler(this, args); + alive_ = args.ContinueRunning; + } + } + + /// + /// Raise the complete file event + /// + /// The file name + private void OnCompleteFile(string file) + { + CompletedFileHandler handler = CompletedFile; + + if (handler != null) + { + var args = new ScanEventArgs(file); + handler(this, args); + alive_ = args.ContinueRunning; + } + } + + /// + /// Raise the ProcessDirectory event. + /// + /// The directory name. + /// Flag indicating if the directory has matching files. + private void OnProcessDirectory(string directory, bool hasMatchingFiles) + { + EventHandler handler = ProcessDirectory; + + if (handler != null) + { + var args = new DirectoryEventArgs(directory, hasMatchingFiles); + handler(this, args); + alive_ = args.ContinueRunning; + } + } + + /// + /// Scan a directory. + /// + /// The base directory to scan. + /// True to recurse subdirectories, false to scan a single directory. + public void Scan(string directory, bool recurse) + { + alive_ = true; + ScanDir(directory, recurse); + } + + private void ScanDir(string directory, bool recurse) + { + try + { + string[] names = System.IO.Directory.GetFiles(directory); + bool hasMatch = false; + for (int fileIndex = 0; fileIndex < names.Length; ++fileIndex) + { + if (!fileFilter_.IsMatch(names[fileIndex])) + { + names[fileIndex] = null; + } + else + { + hasMatch = true; + } + } + + OnProcessDirectory(directory, hasMatch); + + if (alive_ && hasMatch) + { + foreach (string fileName in names) + { + try + { + if (fileName != null) + { + OnProcessFile(fileName); + if (!alive_) + { + break; + } + } + } + catch (Exception e) + { + if (!OnFileFailure(fileName, e)) + { + throw; + } + } + } + } + } + catch (Exception e) + { + if (!OnDirectoryFailure(directory, e)) + { + throw; + } + } + + if (alive_ && recurse) + { + try + { + string[] names = System.IO.Directory.GetDirectories(directory); + foreach (string fulldir in names) + { + if ((directoryFilter_ == null) || (directoryFilter_.IsMatch(fulldir))) + { + ScanDir(fulldir, true); + if (!alive_) + { + break; + } + } + } + } + catch (Exception e) + { + if (!OnDirectoryFailure(directory, e)) + { + throw; + } + } + } + } + + #region Instance Fields + + /// + /// The file filter currently in use. + /// + private IScanFilter fileFilter_; + + /// + /// The directory filter currently in use. + /// + private IScanFilter directoryFilter_; + + /// + /// Flag indicating if scanning should continue running. + /// + private bool alive_; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Core/INameTransform.cs b/ICSharpCode.SharpZipLib/Core/INameTransform.cs new file mode 100644 index 000000000000..492e2a9e6f06 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/INameTransform.cs @@ -0,0 +1,22 @@ +namespace ICSharpCode.SharpZipLib.Core +{ + /// + /// INameTransform defines how file system names are transformed for use with archives, or vice versa. + /// + public interface INameTransform + { + /// + /// Given a file name determine the transformed value. + /// + /// The name to transform. + /// The transformed file name. + string TransformFile(string name); + + /// + /// Given a directory name determine the transformed value. + /// + /// The name to transform. + /// The transformed directory name + string TransformDirectory(string name); + } +} diff --git a/ICSharpCode.SharpZipLib/Core/IScanFilter.cs b/ICSharpCode.SharpZipLib/Core/IScanFilter.cs new file mode 100644 index 000000000000..ac07fd174149 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/IScanFilter.cs @@ -0,0 +1,15 @@ +namespace ICSharpCode.SharpZipLib.Core +{ + /// + /// Scanning filters support filtering of names. + /// + public interface IScanFilter + { + /// + /// Test a name to see if it 'matches' the filter. + /// + /// The name to test. + /// Returns true if the name matches the filter, false if it does not match. + bool IsMatch(string name); + } +} diff --git a/ICSharpCode.SharpZipLib/Core/InvalidNameException.cs b/ICSharpCode.SharpZipLib/Core/InvalidNameException.cs new file mode 100644 index 000000000000..6647631bdd79 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/InvalidNameException.cs @@ -0,0 +1,53 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib.Core +{ + /// + /// InvalidNameException is thrown for invalid names such as directory traversal paths and names with invalid characters + /// + [Serializable] + public class InvalidNameException : SharpZipBaseException + { + /// + /// Initializes a new instance of the InvalidNameException class with a default error message. + /// + public InvalidNameException() : base("An invalid name was specified") + { + } + + /// + /// Initializes a new instance of the InvalidNameException class with a specified error message. + /// + /// A message describing the exception. + public InvalidNameException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the InvalidNameException class with a specified + /// error message and a reference to the inner exception that is the cause of this exception. + /// + /// A message describing the exception. + /// The inner exception + public InvalidNameException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the InvalidNameException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected InvalidNameException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Core/NameFilter.cs b/ICSharpCode.SharpZipLib/Core/NameFilter.cs new file mode 100644 index 000000000000..57751891c657 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/NameFilter.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace ICSharpCode.SharpZipLib.Core +{ + /// + /// NameFilter is a string matching class which allows for both positive and negative + /// matching. + /// A filter is a sequence of independant regular expressions separated by semi-colons ';'. + /// To include a semi-colon it may be quoted as in \;. Each expression can be prefixed by a plus '+' sign or + /// a minus '-' sign to denote the expression is intended to include or exclude names. + /// If neither a plus or minus sign is found include is the default. + /// A given name is tested for inclusion before checking exclusions. Only names matching an include spec + /// and not matching an exclude spec are deemed to match the filter. + /// An empty filter matches any name. + /// + /// The following expression includes all name ending in '.dat' with the exception of 'dummy.dat' + /// "+\.dat$;-^dummy\.dat$" + /// + public class NameFilter : IScanFilter + { + #region Constructors + + /// + /// Construct an instance based on the filter expression passed + /// + /// The filter expression. + public NameFilter(string filter) + { + filter_ = filter; + inclusions_ = new List(); + exclusions_ = new List(); + Compile(); + } + + #endregion Constructors + + /// + /// Test a string to see if it is a valid regular expression. + /// + /// The expression to test. + /// True if expression is a valid false otherwise. + public static bool IsValidExpression(string expression) + { + bool result = true; + try + { + var exp = new Regex(expression, RegexOptions.IgnoreCase | RegexOptions.Singleline); + } + catch (ArgumentException) + { + result = false; + } + return result; + } + + /// + /// Test an expression to see if it is valid as a filter. + /// + /// The filter expression to test. + /// True if the expression is valid, false otherwise. + public static bool IsValidFilterExpression(string toTest) + { + bool result = true; + + try + { + if (toTest != null) + { + string[] items = SplitQuoted(toTest); + for (int i = 0; i < items.Length; ++i) + { + if ((items[i] != null) && (items[i].Length > 0)) + { + string toCompile; + + if (items[i][0] == '+') + { + toCompile = items[i].Substring(1, items[i].Length - 1); + } + else if (items[i][0] == '-') + { + toCompile = items[i].Substring(1, items[i].Length - 1); + } + else + { + toCompile = items[i]; + } + + var testRegex = new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Singleline); + } + } + } + } + catch (ArgumentException) + { + result = false; + } + + return result; + } + + /// + /// Split a string into its component pieces + /// + /// The original string + /// Returns an array of values containing the individual filter elements. + public static string[] SplitQuoted(string original) + { + char escape = '\\'; + char[] separators = { ';' }; + + var result = new List(); + + if (!string.IsNullOrEmpty(original)) + { + int endIndex = -1; + var b = new StringBuilder(); + + while (endIndex < original.Length) + { + endIndex += 1; + if (endIndex >= original.Length) + { + result.Add(b.ToString()); + } + else if (original[endIndex] == escape) + { + endIndex += 1; + if (endIndex >= original.Length) + { + throw new ArgumentException("Missing terminating escape character", nameof(original)); + } + // include escape if this is not an escaped separator + if (Array.IndexOf(separators, original[endIndex]) < 0) + b.Append(escape); + + b.Append(original[endIndex]); + } + else + { + if (Array.IndexOf(separators, original[endIndex]) >= 0) + { + result.Add(b.ToString()); + b.Length = 0; + } + else + { + b.Append(original[endIndex]); + } + } + } + } + + return result.ToArray(); + } + + /// + /// Convert this filter to its string equivalent. + /// + /// The string equivalent for this filter. + public override string ToString() + { + return filter_; + } + + /// + /// Test a value to see if it is included by the filter. + /// + /// The value to test. + /// True if the value is included, false otherwise. + public bool IsIncluded(string name) + { + bool result = false; + if (inclusions_.Count == 0) + { + result = true; + } + else + { + foreach (Regex r in inclusions_) + { + if (r.IsMatch(name)) + { + result = true; + break; + } + } + } + return result; + } + + /// + /// Test a value to see if it is excluded by the filter. + /// + /// The value to test. + /// True if the value is excluded, false otherwise. + public bool IsExcluded(string name) + { + bool result = false; + foreach (Regex r in exclusions_) + { + if (r.IsMatch(name)) + { + result = true; + break; + } + } + return result; + } + + #region IScanFilter Members + + /// + /// Test a value to see if it matches the filter. + /// + /// The value to test. + /// True if the value matches, false otherwise. + public bool IsMatch(string name) + { + return (IsIncluded(name) && !IsExcluded(name)); + } + + #endregion IScanFilter Members + + /// + /// Compile this filter. + /// + private void Compile() + { + // TODO: Check to see if combining RE's makes it faster/smaller. + // simple scheme would be to have one RE for inclusion and one for exclusion. + if (filter_ == null) + { + return; + } + + string[] items = SplitQuoted(filter_); + for (int i = 0; i < items.Length; ++i) + { + if ((items[i] != null) && (items[i].Length > 0)) + { + bool include = (items[i][0] != '-'); + string toCompile; + + if (items[i][0] == '+') + { + toCompile = items[i].Substring(1, items[i].Length - 1); + } + else if (items[i][0] == '-') + { + toCompile = items[i].Substring(1, items[i].Length - 1); + } + else + { + toCompile = items[i]; + } + + // NOTE: Regular expressions can fail to compile here for a number of reasons that cause an exception + // these are left unhandled here as the caller is responsible for ensuring all is valid. + // several functions IsValidFilterExpression and IsValidExpression are provided for such checking + if (include) + { + inclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)); + } + else + { + exclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)); + } + } + } + } + + #region Instance Fields + + private string filter_; + private List inclusions_; + private List exclusions_; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Core/PathFilter.cs b/ICSharpCode.SharpZipLib/Core/PathFilter.cs new file mode 100644 index 000000000000..e70109c2c99c --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/PathFilter.cs @@ -0,0 +1,318 @@ +using System; +using System.IO; + +namespace ICSharpCode.SharpZipLib.Core +{ + /// + /// PathFilter filters directories and files using a form of regular expressions + /// by full path name. + /// See NameFilter for more detail on filtering. + /// + public class PathFilter : IScanFilter + { + #region Constructors + + /// + /// Initialise a new instance of . + /// + /// The filter expression to apply. + public PathFilter(string filter) + { + nameFilter_ = new NameFilter(filter); + } + + #endregion Constructors + + #region IScanFilter Members + + /// + /// Test a name to see if it matches the filter. + /// + /// The name to test. + /// True if the name matches, false otherwise. + /// is used to get the full path before matching. + public virtual bool IsMatch(string name) + { + bool result = false; + + if (name != null) + { + string cooked = (name.Length > 0) ? Path.GetFullPath(name) : ""; + result = nameFilter_.IsMatch(cooked); + } + return result; + } + + private readonly + + #endregion IScanFilter Members + + #region Instance Fields + + NameFilter nameFilter_; + + #endregion Instance Fields + } + + /// + /// ExtendedPathFilter filters based on name, file size, and the last write time of the file. + /// + /// Provides an example of how to customise filtering. + public class ExtendedPathFilter : PathFilter + { + #region Constructors + + /// + /// Initialise a new instance of ExtendedPathFilter. + /// + /// The filter to apply. + /// The minimum file size to include. + /// The maximum file size to include. + public ExtendedPathFilter(string filter, + long minSize, long maxSize) + : base(filter) + { + MinSize = minSize; + MaxSize = maxSize; + } + + /// + /// Initialise a new instance of ExtendedPathFilter. + /// + /// The filter to apply. + /// The minimum to include. + /// The maximum to include. + public ExtendedPathFilter(string filter, + DateTime minDate, DateTime maxDate) + : base(filter) + { + MinDate = minDate; + MaxDate = maxDate; + } + + /// + /// Initialise a new instance of ExtendedPathFilter. + /// + /// The filter to apply. + /// The minimum file size to include. + /// The maximum file size to include. + /// The minimum to include. + /// The maximum to include. + public ExtendedPathFilter(string filter, + long minSize, long maxSize, + DateTime minDate, DateTime maxDate) + : base(filter) + { + MinSize = minSize; + MaxSize = maxSize; + MinDate = minDate; + MaxDate = maxDate; + } + + #endregion Constructors + + #region IScanFilter Members + + /// + /// Test a filename to see if it matches the filter. + /// + /// The filename to test. + /// True if the filter matches, false otherwise. + /// The doesnt exist + public override bool IsMatch(string name) + { + bool result = base.IsMatch(name); + + if (result) + { + var fileInfo = new FileInfo(name); + result = + (MinSize <= fileInfo.Length) && + (MaxSize >= fileInfo.Length) && + (MinDate <= fileInfo.LastWriteTime) && + (MaxDate >= fileInfo.LastWriteTime) + ; + } + return result; + } + + #endregion IScanFilter Members + + #region Properties + + /// + /// Get/set the minimum size/length for a file that will match this filter. + /// + /// The default value is zero. + /// value is less than zero; greater than + public long MinSize + { + get { return minSize_; } + set + { + if ((value < 0) || (maxSize_ < value)) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + minSize_ = value; + } + } + + /// + /// Get/set the maximum size/length for a file that will match this filter. + /// + /// The default value is + /// value is less than zero or less than + public long MaxSize + { + get { return maxSize_; } + set + { + if ((value < 0) || (minSize_ > value)) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + maxSize_ = value; + } + } + + /// + /// Get/set the minimum value that will match for this filter. + /// + /// Files with a LastWrite time less than this value are excluded by the filter. + public DateTime MinDate + { + get + { + return minDate_; + } + + set + { + if (value > maxDate_) + { + throw new ArgumentOutOfRangeException(nameof(value), "Exceeds MaxDate"); + } + + minDate_ = value; + } + } + + /// + /// Get/set the maximum value that will match for this filter. + /// + /// Files with a LastWrite time greater than this value are excluded by the filter. + public DateTime MaxDate + { + get + { + return maxDate_; + } + + set + { + if (minDate_ > value) + { + throw new ArgumentOutOfRangeException(nameof(value), "Exceeds MinDate"); + } + + maxDate_ = value; + } + } + + #endregion Properties + + #region Instance Fields + + private long minSize_; + private long maxSize_ = long.MaxValue; + private DateTime minDate_ = DateTime.MinValue; + private DateTime maxDate_ = DateTime.MaxValue; + + #endregion Instance Fields + } + + /// + /// NameAndSizeFilter filters based on name and file size. + /// + /// A sample showing how filters might be extended. + [Obsolete("Use ExtendedPathFilter instead")] + public class NameAndSizeFilter : PathFilter + { + /// + /// Initialise a new instance of NameAndSizeFilter. + /// + /// The filter to apply. + /// The minimum file size to include. + /// The maximum file size to include. + public NameAndSizeFilter(string filter, long minSize, long maxSize) + : base(filter) + { + MinSize = minSize; + MaxSize = maxSize; + } + + /// + /// Test a filename to see if it matches the filter. + /// + /// The filename to test. + /// True if the filter matches, false otherwise. + public override bool IsMatch(string name) + { + bool result = base.IsMatch(name); + + if (result) + { + var fileInfo = new FileInfo(name); + long length = fileInfo.Length; + result = + (MinSize <= length) && + (MaxSize >= length); + } + return result; + } + + /// + /// Get/set the minimum size for a file that will match this filter. + /// + public long MinSize + { + get { return minSize_; } + set + { + if ((value < 0) || (maxSize_ < value)) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + minSize_ = value; + } + } + + /// + /// Get/set the maximum size for a file that will match this filter. + /// + public long MaxSize + { + get { return maxSize_; } + set + { + if ((value < 0) || (minSize_ > value)) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + maxSize_ = value; + } + } + + #region Instance Fields + + private long minSize_; + private long maxSize_ = long.MaxValue; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Core/PathUtils.cs b/ICSharpCode.SharpZipLib/Core/PathUtils.cs new file mode 100644 index 000000000000..b8d0dd409f9f --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/PathUtils.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; +using System.Linq; + +namespace ICSharpCode.SharpZipLib.Core +{ + /// + /// PathUtils provides simple utilities for handling paths. + /// + public static class PathUtils + { + /// + /// Remove any path root present in the path + /// + /// A containing path information. + /// The path with the root removed if it was present; path otherwise. + public static string DropPathRoot(string path) + { + var invalidChars = Path.GetInvalidPathChars(); + // If the first character after the root is a ':', .NET < 4.6.2 throws + var cleanRootSep = path.Length >= 3 && path[1] == ':' && path[2] == ':'; + + // Replace any invalid path characters with '_' to prevent Path.GetPathRoot from throwing. + // Only pass the first 258 (should be 260, but that still throws for some reason) characters + // as .NET < 4.6.2 throws on longer paths + var cleanPath = new string(path.Take(258) + .Select( (c, i) => invalidChars.Contains(c) || (i == 2 && cleanRootSep) ? '_' : c).ToArray()); + + var stripLength = Path.GetPathRoot(cleanPath).Length; + while (path.Length > stripLength && (path[stripLength] == '/' || path[stripLength] == '\\')) stripLength++; + return path.Substring(stripLength); + } + + /// + /// Returns a random file name in the users temporary directory, or in directory of if specified + /// + /// If specified, used as the base file name for the temporary file + /// Returns a temporary file name + public static string GetTempFileName(string original = null) + { + string fileName; + var tempPath = Path.GetTempPath(); + + do + { + fileName = original == null + ? Path.Combine(tempPath, Path.GetRandomFileName()) + : $"{original}.{Path.GetRandomFileName()}"; + } while (File.Exists(fileName)); + + return fileName; + } + } +} diff --git a/ICSharpCode.SharpZipLib/Core/StreamUtils.cs b/ICSharpCode.SharpZipLib/Core/StreamUtils.cs new file mode 100644 index 000000000000..47de6e26ecd5 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Core/StreamUtils.cs @@ -0,0 +1,295 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace ICSharpCode.SharpZipLib.Core +{ + /// + /// Provides simple " utilities. + /// + public static class StreamUtils + { + /// + /// Read from a ensuring all the required data is read. + /// + /// The stream to read. + /// The buffer to fill. + /// + public static void ReadFully(Stream stream, byte[] buffer) + { + ReadFully(stream, buffer, 0, buffer.Length); + } + + /// + /// Read from a " ensuring all the required data is read. + /// + /// The stream to read data from. + /// The buffer to store data in. + /// The offset at which to begin storing data. + /// The number of bytes of data to store. + /// Required parameter is null + /// and or are invalid. + /// End of stream is encountered before all the data has been read. + public static void ReadFully(Stream stream, byte[] buffer, int offset, int count) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + // Offset can equal length when buffer and count are 0. + if ((offset < 0) || (offset > buffer.Length)) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + if ((count < 0) || (offset + count > buffer.Length)) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + while (count > 0) + { + int readCount = stream.Read(buffer, offset, count); + if (readCount <= 0) + { + throw new EndOfStreamException(); + } + offset += readCount; + count -= readCount; + } + } + + /// + /// Read as much data as possible from a ", up to the requested number of bytes + /// + /// The stream to read data from. + /// The buffer to store data in. + /// The offset at which to begin storing data. + /// The number of bytes of data to store. + /// Required parameter is null + /// and or are invalid. + public static int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, int count) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + // Offset can equal length when buffer and count are 0. + if ((offset < 0) || (offset > buffer.Length)) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + if ((count < 0) || (offset + count > buffer.Length)) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + int totalReadCount = 0; + while (count > 0) + { + int readCount = stream.Read(buffer, offset, count); + if (readCount <= 0) + { + break; + } + offset += readCount; + count -= readCount; + totalReadCount += readCount; + } + + return totalReadCount; + } + + /// + /// Copy the contents of one to another. + /// + /// The stream to source data from. + /// The stream to write data to. + /// The buffer to use during copying. + public static void Copy(Stream source, Stream destination, byte[] buffer) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + // Ensure a reasonable size of buffer is used without being prohibitive. + if (buffer.Length < 128) + { + throw new ArgumentException("Buffer is too small", nameof(buffer)); + } + + bool copying = true; + + while (copying) + { + int bytesRead = source.Read(buffer, 0, buffer.Length); + if (bytesRead > 0) + { + destination.Write(buffer, 0, bytesRead); + } + else + { + destination.Flush(); + copying = false; + } + } + } + + /// + /// Copy the contents of one to another. + /// + /// The stream to source data from. + /// The stream to write data to. + /// The buffer to use during copying. + /// The progress handler delegate to use. + /// The minimum between progress updates. + /// The source for this event. + /// The name to use with the event. + /// This form is specialised for use within #Zip to support events during archive operations. + public static void Copy(Stream source, Stream destination, + byte[] buffer, ProgressHandler progressHandler, TimeSpan updateInterval, object sender, string name) + { + Copy(source, destination, buffer, progressHandler, updateInterval, sender, name, -1); + } + + /// + /// Copy the contents of one to another. + /// + /// The stream to source data from. + /// The stream to write data to. + /// The buffer to use during copying. + /// The progress handler delegate to use. + /// The minimum between progress updates. + /// The source for this event. + /// The name to use with the event. + /// A predetermined fixed target value to use with progress updates. + /// If the value is negative the target is calculated by looking at the stream. + /// This form is specialised for use within #Zip to support events during archive operations. + public static void Copy(Stream source, Stream destination, + byte[] buffer, + ProgressHandler progressHandler, TimeSpan updateInterval, + object sender, string name, long fixedTarget) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + // Ensure a reasonable size of buffer is used without being prohibitive. + if (buffer.Length < 128) + { + throw new ArgumentException("Buffer is too small", nameof(buffer)); + } + + if (progressHandler == null) + { + throw new ArgumentNullException(nameof(progressHandler)); + } + + bool copying = true; + + DateTime marker = DateTime.Now; + long processed = 0; + long target = 0; + + if (fixedTarget >= 0) + { + target = fixedTarget; + } + else if (source.CanSeek) + { + target = source.Length - source.Position; + } + + // Always fire 0% progress.. + var args = new ProgressEventArgs(name, processed, target); + progressHandler(sender, args); + + bool progressFired = true; + + while (copying) + { + int bytesRead = source.Read(buffer, 0, buffer.Length); + if (bytesRead > 0) + { + processed += bytesRead; + progressFired = false; + destination.Write(buffer, 0, bytesRead); + } + else + { + destination.Flush(); + copying = false; + } + + if (DateTime.Now - marker > updateInterval) + { + progressFired = true; + marker = DateTime.Now; + args = new ProgressEventArgs(name, processed, target); + progressHandler(sender, args); + + copying = args.ContinueRunning; + } + } + + if (!progressFired) + { + args = new ProgressEventArgs(name, processed, target); + progressHandler(sender, args); + } + } + + internal static async Task WriteProcToStreamAsync(this Stream targetStream, MemoryStream bufferStream, Action writeProc, CancellationToken ct) + { + bufferStream.SetLength(0); + writeProc(bufferStream); + bufferStream.Position = 0; + await bufferStream.CopyToAsync(targetStream, 81920, ct); + bufferStream.SetLength(0); + } + + internal static async Task WriteProcToStreamAsync(this Stream targetStream, Action writeProc, CancellationToken ct) + { + using (var ms = new MemoryStream()) + { + await WriteProcToStreamAsync(targetStream, ms, writeProc, ct); + } + } + } +} diff --git a/ICSharpCode.SharpZipLib/Encryption/PkzipClassic.cs b/ICSharpCode.SharpZipLib/Encryption/PkzipClassic.cs new file mode 100644 index 000000000000..6730c9dee181 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Encryption/PkzipClassic.cs @@ -0,0 +1,487 @@ +using ICSharpCode.SharpZipLib.Checksum; +using System; +using System.Security.Cryptography; + +namespace ICSharpCode.SharpZipLib.Encryption +{ + /// + /// PkzipClassic embodies the classic or original encryption facilities used in Pkzip archives. + /// While it has been superceded by more recent and more powerful algorithms, its still in use and + /// is viable for preventing casual snooping + /// + public abstract class PkzipClassic : SymmetricAlgorithm + { + /// + /// Generates new encryption keys based on given seed + /// + /// The seed value to initialise keys with. + /// A new key value. + static public byte[] GenerateKeys(byte[] seed) + { + if (seed == null) + { + throw new ArgumentNullException(nameof(seed)); + } + + if (seed.Length == 0) + { + throw new ArgumentException("Length is zero", nameof(seed)); + } + + uint[] newKeys = { + 0x12345678, + 0x23456789, + 0x34567890 + }; + + for (int i = 0; i < seed.Length; ++i) + { + newKeys[0] = Crc32.ComputeCrc32(newKeys[0], seed[i]); + newKeys[1] = newKeys[1] + (byte)newKeys[0]; + newKeys[1] = newKeys[1] * 134775813 + 1; + newKeys[2] = Crc32.ComputeCrc32(newKeys[2], (byte)(newKeys[1] >> 24)); + } + + byte[] result = new byte[12]; + result[0] = (byte)(newKeys[0] & 0xff); + result[1] = (byte)((newKeys[0] >> 8) & 0xff); + result[2] = (byte)((newKeys[0] >> 16) & 0xff); + result[3] = (byte)((newKeys[0] >> 24) & 0xff); + result[4] = (byte)(newKeys[1] & 0xff); + result[5] = (byte)((newKeys[1] >> 8) & 0xff); + result[6] = (byte)((newKeys[1] >> 16) & 0xff); + result[7] = (byte)((newKeys[1] >> 24) & 0xff); + result[8] = (byte)(newKeys[2] & 0xff); + result[9] = (byte)((newKeys[2] >> 8) & 0xff); + result[10] = (byte)((newKeys[2] >> 16) & 0xff); + result[11] = (byte)((newKeys[2] >> 24) & 0xff); + return result; + } + } + + /// + /// PkzipClassicCryptoBase provides the low level facilities for encryption + /// and decryption using the PkzipClassic algorithm. + /// + internal class PkzipClassicCryptoBase + { + /// + /// Transform a single byte + /// + /// + /// The transformed value + /// + protected byte TransformByte() + { + uint temp = ((keys[2] & 0xFFFF) | 2); + return (byte)((temp * (temp ^ 1)) >> 8); + } + + /// + /// Set the key schedule for encryption/decryption. + /// + /// The data use to set the keys from. + protected void SetKeys(byte[] keyData) + { + if (keyData == null) + { + throw new ArgumentNullException(nameof(keyData)); + } + + if (keyData.Length != 12) + { + throw new InvalidOperationException("Key length is not valid"); + } + + keys = new uint[3]; + keys[0] = (uint)((keyData[3] << 24) | (keyData[2] << 16) | (keyData[1] << 8) | keyData[0]); + keys[1] = (uint)((keyData[7] << 24) | (keyData[6] << 16) | (keyData[5] << 8) | keyData[4]); + keys[2] = (uint)((keyData[11] << 24) | (keyData[10] << 16) | (keyData[9] << 8) | keyData[8]); + } + + /// + /// Update encryption keys + /// + protected void UpdateKeys(byte ch) + { + keys[0] = Crc32.ComputeCrc32(keys[0], ch); + keys[1] = keys[1] + (byte)keys[0]; + keys[1] = keys[1] * 134775813 + 1; + keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24)); + } + + /// + /// Reset the internal state. + /// + protected void Reset() + { + keys[0] = 0; + keys[1] = 0; + keys[2] = 0; + } + + #region Instance Fields + + private uint[] keys; + + #endregion Instance Fields + } + + /// + /// PkzipClassic CryptoTransform for encryption. + /// + internal class PkzipClassicEncryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform + { + /// + /// Initialise a new instance of + /// + /// The key block to use. + internal PkzipClassicEncryptCryptoTransform(byte[] keyBlock) + { + SetKeys(keyBlock); + } + + #region ICryptoTransform Members + + /// + /// Transforms the specified region of the specified byte array. + /// + /// The input for which to compute the transform. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the byte array to use as data. + /// The computed transform. + public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) + { + byte[] result = new byte[inputCount]; + TransformBlock(inputBuffer, inputOffset, inputCount, result, 0); + return result; + } + + /// + /// Transforms the specified region of the input byte array and copies + /// the resulting transform to the specified region of the output byte array. + /// + /// The input for which to compute the transform. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write the transform. + /// The offset into the output byte array from which to begin writing data. + /// The number of bytes written. + public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + for (int i = inputOffset; i < inputOffset + inputCount; ++i) + { + byte oldbyte = inputBuffer[i]; + outputBuffer[outputOffset++] = (byte)(inputBuffer[i] ^ TransformByte()); + UpdateKeys(oldbyte); + } + return inputCount; + } + + /// + /// Gets a value indicating whether the current transform can be reused. + /// + public bool CanReuseTransform + { + get + { + return true; + } + } + + /// + /// Gets the size of the input data blocks in bytes. + /// + public int InputBlockSize + { + get + { + return 1; + } + } + + /// + /// Gets the size of the output data blocks in bytes. + /// + public int OutputBlockSize + { + get + { + return 1; + } + } + + /// + /// Gets a value indicating whether multiple blocks can be transformed. + /// + public bool CanTransformMultipleBlocks + { + get + { + return true; + } + } + + #endregion ICryptoTransform Members + + #region IDisposable Members + + /// + /// Cleanup internal state. + /// + public void Dispose() + { + Reset(); + } + + #endregion IDisposable Members + } + + /// + /// PkzipClassic CryptoTransform for decryption. + /// + internal class PkzipClassicDecryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform + { + /// + /// Initialise a new instance of . + /// + /// The key block to decrypt with. + internal PkzipClassicDecryptCryptoTransform(byte[] keyBlock) + { + SetKeys(keyBlock); + } + + #region ICryptoTransform Members + + /// + /// Transforms the specified region of the specified byte array. + /// + /// The input for which to compute the transform. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the byte array to use as data. + /// The computed transform. + public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) + { + byte[] result = new byte[inputCount]; + TransformBlock(inputBuffer, inputOffset, inputCount, result, 0); + return result; + } + + /// + /// Transforms the specified region of the input byte array and copies + /// the resulting transform to the specified region of the output byte array. + /// + /// The input for which to compute the transform. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write the transform. + /// The offset into the output byte array from which to begin writing data. + /// The number of bytes written. + public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + for (int i = inputOffset; i < inputOffset + inputCount; ++i) + { + var newByte = (byte)(inputBuffer[i] ^ TransformByte()); + outputBuffer[outputOffset++] = newByte; + UpdateKeys(newByte); + } + return inputCount; + } + + /// + /// Gets a value indicating whether the current transform can be reused. + /// + public bool CanReuseTransform + { + get + { + return true; + } + } + + /// + /// Gets the size of the input data blocks in bytes. + /// + public int InputBlockSize + { + get + { + return 1; + } + } + + /// + /// Gets the size of the output data blocks in bytes. + /// + public int OutputBlockSize + { + get + { + return 1; + } + } + + /// + /// Gets a value indicating whether multiple blocks can be transformed. + /// + public bool CanTransformMultipleBlocks + { + get + { + return true; + } + } + + #endregion ICryptoTransform Members + + #region IDisposable Members + + /// + /// Cleanup internal state. + /// + public void Dispose() + { + Reset(); + } + + #endregion IDisposable Members + } + + /// + /// Defines a wrapper object to access the Pkzip algorithm. + /// This class cannot be inherited. + /// + public sealed class PkzipClassicManaged : PkzipClassic + { + /// + /// Get / set the applicable block size in bits. + /// + /// The only valid block size is 8. + public override int BlockSize + { + get + { + return 8; + } + + set + { + if (value != 8) + { + throw new CryptographicException("Block size is invalid"); + } + } + } + + /// + /// Get an array of legal key sizes. + /// + public override KeySizes[] LegalKeySizes + { + get + { + KeySizes[] keySizes = new KeySizes[1]; + keySizes[0] = new KeySizes(12 * 8, 12 * 8, 0); + return keySizes; + } + } + + /// + /// Generate an initial vector. + /// + public override void GenerateIV() + { + // Do nothing. + } + + /// + /// Get an array of legal block sizes. + /// + public override KeySizes[] LegalBlockSizes + { + get + { + KeySizes[] keySizes = new KeySizes[1]; + keySizes[0] = new KeySizes(1 * 8, 1 * 8, 0); + return keySizes; + } + } + + /// + /// Get / set the key value applicable. + /// + public override byte[] Key + { + get + { + if (key_ == null) + { + GenerateKey(); + } + + return (byte[])key_.Clone(); + } + + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (value.Length != 12) + { + throw new CryptographicException("Key size is illegal"); + } + + key_ = (byte[])value.Clone(); + } + } + + /// + /// Generate a new random key. + /// + public override void GenerateKey() + { + key_ = new byte[12]; + using (var rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(key_); + } + } + + /// + /// Create an encryptor. + /// + /// The key to use for this encryptor. + /// Initialisation vector for the new encryptor. + /// Returns a new PkzipClassic encryptor + public override ICryptoTransform CreateEncryptor( + byte[] rgbKey, + byte[] rgbIV) + { + key_ = rgbKey; + return new PkzipClassicEncryptCryptoTransform(Key); + } + + /// + /// Create a decryptor. + /// + /// Keys to use for this new decryptor. + /// Initialisation vector for the new decryptor. + /// Returns a new decryptor. + public override ICryptoTransform CreateDecryptor( + byte[] rgbKey, + byte[] rgbIV) + { + key_ = rgbKey; + return new PkzipClassicDecryptCryptoTransform(Key); + } + + #region Instance Fields + + private byte[] key_; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs b/ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs new file mode 100644 index 000000000000..80ce0b4ab237 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs @@ -0,0 +1,230 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; +using ICSharpCode.SharpZipLib.Core; +using ICSharpCode.SharpZipLib.Zip; + +namespace ICSharpCode.SharpZipLib.Encryption +{ + /// + /// Encrypts and decrypts AES ZIP + /// + /// + /// Based on information from http://www.winzip.com/aes_info.htm + /// and http://www.gladman.me.uk/cryptography_technology/fileencrypt/ + /// + internal class ZipAESStream : CryptoStream + { + /// + /// Constructor + /// + /// The stream on which to perform the cryptographic transformation. + /// Instance of ZipAESTransform + /// Read or Write + public ZipAESStream(Stream stream, ZipAESTransform transform, CryptoStreamMode mode) + : base(stream, transform, mode) + { + _stream = stream; + _transform = transform; + _slideBuffer = new byte[1024]; + + // mode: + // CryptoStreamMode.Read means we read from "stream" and pass decrypted to our Read() method. + // Write bypasses this stream and uses the Transform directly. + if (mode != CryptoStreamMode.Read) + { + throw new Exception("ZipAESStream only for read"); + } + } + + // The final n bytes of the AES stream contain the Auth Code. + private const int AUTH_CODE_LENGTH = 10; + + // Blocksize is always 16 here, even for AES-256 which has transform.InputBlockSize of 32. + private const int CRYPTO_BLOCK_SIZE = 16; + + // total length of block + auth code + private const int BLOCK_AND_AUTH = CRYPTO_BLOCK_SIZE + AUTH_CODE_LENGTH; + + private Stream _stream; + private ZipAESTransform _transform; + private byte[] _slideBuffer; + private int _slideBufStartPos; + private int _slideBufFreePos; + + // Buffer block transforms to enable partial reads + private byte[] _transformBuffer = null;// new byte[CRYPTO_BLOCK_SIZE]; + private int _transformBufferFreePos; + private int _transformBufferStartPos; + + // Do we have some buffered data available? + private bool HasBufferedData =>_transformBuffer != null && _transformBufferStartPos < _transformBufferFreePos; + + /// + /// Reads a sequence of bytes from the current CryptoStream into buffer, + /// and advances the position within the stream by the number of bytes read. + /// + public override int Read(byte[] buffer, int offset, int count) + { + // Nothing to do + if (count == 0) + return 0; + + // If we have buffered data, read that first + int nBytes = 0; + if (HasBufferedData) + { + nBytes = ReadBufferedData(buffer, offset, count); + + // Read all requested data from the buffer + if (nBytes == count) + return nBytes; + + offset += nBytes; + count -= nBytes; + } + + // Read more data from the input, if available + if (_slideBuffer != null) + nBytes += ReadAndTransform(buffer, offset, count); + + return nBytes; + } + + /// + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + var readCount = Read(buffer, offset, count); + return Task.FromResult(readCount); + } + + // Read data from the underlying stream and decrypt it + private int ReadAndTransform(byte[] buffer, int offset, int count) + { + int nBytes = 0; + while (nBytes < count) + { + int bytesLeftToRead = count - nBytes; + + // Calculate buffer quantities vs read-ahead size, and check for sufficient free space + int byteCount = _slideBufFreePos - _slideBufStartPos; + + // Need to handle final block and Auth Code specially, but don't know total data length. + // Maintain a read-ahead equal to the length of (crypto block + Auth Code). + // When that runs out we can detect these final sections. + int lengthToRead = BLOCK_AND_AUTH - byteCount; + if (_slideBuffer.Length - _slideBufFreePos < lengthToRead) + { + // Shift the data to the beginning of the buffer + int iTo = 0; + for (int iFrom = _slideBufStartPos; iFrom < _slideBufFreePos; iFrom++, iTo++) + { + _slideBuffer[iTo] = _slideBuffer[iFrom]; + } + _slideBufFreePos -= _slideBufStartPos; // Note the -= + _slideBufStartPos = 0; + } + int obtained = StreamUtils.ReadRequestedBytes(_stream, _slideBuffer, _slideBufFreePos, lengthToRead); + _slideBufFreePos += obtained; + + // Recalculate how much data we now have + byteCount = _slideBufFreePos - _slideBufStartPos; + if (byteCount >= BLOCK_AND_AUTH) + { + var read = TransformAndBufferBlock(buffer, offset, bytesLeftToRead, CRYPTO_BLOCK_SIZE); + nBytes += read; + offset += read; + } + else + { + // Last round. + if (byteCount > AUTH_CODE_LENGTH) + { + // At least one byte of data plus auth code + int finalBlock = byteCount - AUTH_CODE_LENGTH; + nBytes += TransformAndBufferBlock(buffer, offset, bytesLeftToRead, finalBlock); + } + else if (byteCount < AUTH_CODE_LENGTH) + throw new ZipException("Internal error missed auth code"); // Coding bug + // Final block done. Check Auth code. + byte[] calcAuthCode = _transform.GetAuthCode(); + for (int i = 0; i < AUTH_CODE_LENGTH; i++) + { + if (calcAuthCode[i] != _slideBuffer[_slideBufStartPos + i]) + { + throw new ZipException("AES Authentication Code does not match. This is a super-CRC check on the data in the file after compression and encryption. \r\n" + + "The file may be damaged."); + } + } + + // don't need this any more, so use it as a 'complete' flag + _slideBuffer = null; + + break; // Reached the auth code + } + } + return nBytes; + } + + // read some buffered data + private int ReadBufferedData(byte[] buffer, int offset, int count) + { + int copyCount = Math.Min(count, _transformBufferFreePos - _transformBufferStartPos); + + Array.Copy(_transformBuffer, _transformBufferStartPos, buffer, offset, copyCount); + _transformBufferStartPos += copyCount; + + return copyCount; + } + + // Perform the crypto transform, and buffer the data if less than one block has been requested. + private int TransformAndBufferBlock(byte[] buffer, int offset, int count, int blockSize) + { + // If the requested data is greater than one block, transform it directly into the output + // If it's smaller, do it into a temporary buffer and copy the requested part + bool bufferRequired = (blockSize > count); + + if (bufferRequired && _transformBuffer == null) + _transformBuffer = new byte[CRYPTO_BLOCK_SIZE]; + + var targetBuffer = bufferRequired ? _transformBuffer : buffer; + var targetOffset = bufferRequired ? 0 : offset; + + // Transform the data + _transform.TransformBlock(_slideBuffer, + _slideBufStartPos, + blockSize, + targetBuffer, + targetOffset); + + _slideBufStartPos += blockSize; + + if (!bufferRequired) + { + return blockSize; + } + else + { + Array.Copy(_transformBuffer, 0, buffer, offset, count); + _transformBufferStartPos = count; + _transformBufferFreePos = blockSize; + + return count; + } + } + + /// + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// An array of bytes. This method copies count bytes from buffer to the current stream. + /// The byte offset in buffer at which to begin copying bytes to the current stream. + /// The number of bytes to be written to the current stream. + public override void Write(byte[] buffer, int offset, int count) + { + // ZipAESStream is used for reading but not for writing. Writing uses the ZipAESTransform directly. + throw new NotImplementedException(); + } + } +} diff --git a/ICSharpCode.SharpZipLib/Encryption/ZipAESTransform.cs b/ICSharpCode.SharpZipLib/Encryption/ZipAESTransform.cs new file mode 100644 index 000000000000..5aced2d71901 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Encryption/ZipAESTransform.cs @@ -0,0 +1,224 @@ +using System; +using System.Security.Cryptography; +using ICSharpCode.SharpZipLib.Core; + +namespace ICSharpCode.SharpZipLib.Encryption +{ + /// + /// Transforms stream using AES in CTR mode + /// + internal class ZipAESTransform : ICryptoTransform + { +#if NET45 + class IncrementalHash : HMACSHA1 + { + bool _finalised; + public IncrementalHash(byte[] key) : base(key) { } + public static IncrementalHash CreateHMAC(string n, byte[] key) => new IncrementalHash(key); + public void AppendData(byte[] buffer, int offset, int count) => TransformBlock(buffer, offset, count, buffer, offset); + public byte[] GetHashAndReset() + { + if (!_finalised) + { + byte[] dummy = new byte[0]; + TransformFinalBlock(dummy, 0, 0); + _finalised = true; + } + return Hash; + } + } + + static class HashAlgorithmName + { + public static string SHA1 = null; + } +#endif + + private const int PWD_VER_LENGTH = 2; + + // WinZip use iteration count of 1000 for PBKDF2 key generation + private const int KEY_ROUNDS = 1000; + + // For 128-bit AES (16 bytes) the encryption is implemented as expected. + // For 256-bit AES (32 bytes) WinZip do full 256 bit AES of the nonce to create the encryption + // block but use only the first 16 bytes of it, and discard the second half. + private const int ENCRYPT_BLOCK = 16; + + private int _blockSize; + private readonly ICryptoTransform _encryptor; + private readonly byte[] _counterNonce; + private byte[] _encryptBuffer; + private int _encrPos; + private byte[] _pwdVerifier; + private IncrementalHash _hmacsha1; + private byte[] _authCode = null; + + private bool _writeMode; + + /// + /// Constructor. + /// + /// Password string + /// Random bytes, length depends on encryption strength. + /// 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes. + /// The encryption strength, in bytes eg 16 for 128 bits. + /// True when creating a zip, false when reading. For the AuthCode. + /// + public ZipAESTransform(string key, byte[] saltBytes, int blockSize, bool writeMode) + { + if (blockSize != 16 && blockSize != 32) // 24 valid for AES but not supported by Winzip + throw new Exception("Invalid blocksize " + blockSize + ". Must be 16 or 32."); + if (saltBytes.Length != blockSize / 2) + throw new Exception("Invalid salt len. Must be " + blockSize / 2 + " for blocksize " + blockSize); + // initialise the encryption buffer and buffer pos + _blockSize = blockSize; + _encryptBuffer = new byte[_blockSize]; + _encrPos = ENCRYPT_BLOCK; + + // Performs the equivalent of derive_key in Dr Brian Gladman's pwd2key.c + var pdb = new Rfc2898DeriveBytes(key, saltBytes, KEY_ROUNDS); + var rm = Aes.Create(); + rm.Mode = CipherMode.ECB; // No feedback from cipher for CTR mode + _counterNonce = new byte[_blockSize]; + byte[] key1bytes = pdb.GetBytes(_blockSize); + byte[] key2bytes = pdb.GetBytes(_blockSize); + + // Use empty IV for AES + _encryptor = rm.CreateEncryptor(key1bytes, new byte[16]); + _pwdVerifier = pdb.GetBytes(PWD_VER_LENGTH); + // + _hmacsha1 = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA1, key2bytes); + _writeMode = writeMode; + } + + /// + /// Implement the ICryptoTransform method. + /// + public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + // Pass the data stream to the hash algorithm for generating the Auth Code. + // This does not change the inputBuffer. Do this before decryption for read mode. + if (!_writeMode) + { + _hmacsha1.AppendData(inputBuffer, inputOffset, inputCount); + } + // Encrypt with AES in CTR mode. Regards to Dr Brian Gladman for this. + int ix = 0; + while (ix < inputCount) + { + if (_encrPos == ENCRYPT_BLOCK) + { + /* increment encryption nonce */ + int j = 0; + while (++_counterNonce[j] == 0) + { + ++j; + } + /* encrypt the nonce to form next xor buffer */ + _encryptor.TransformBlock(_counterNonce, 0, _blockSize, _encryptBuffer, 0); + _encrPos = 0; + } + outputBuffer[ix + outputOffset] = (byte)(inputBuffer[ix + inputOffset] ^ _encryptBuffer[_encrPos++]); + // + ix++; + } + if (_writeMode) + { + // This does not change the buffer. + _hmacsha1.AppendData(outputBuffer, outputOffset, inputCount); + } + return inputCount; + } + + /// + /// Returns the 2 byte password verifier + /// + public byte[] PwdVerifier + { + get + { + return _pwdVerifier; + } + } + + /// + /// Returns the 10 byte AUTH CODE to be checked or appended immediately following the AES data stream. + /// + public byte[] GetAuthCode() + { + if (_authCode == null) + { + _authCode = _hmacsha1.GetHashAndReset(); + } + return _authCode; + } + + #region ICryptoTransform Members + + /// + /// Not implemented. + /// + public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) + { + if(inputCount > 0) + { + throw new NotImplementedException("TransformFinalBlock is not implemented and inputCount is greater than 0"); + } + return Empty.Array(); + } + + /// + /// Gets the size of the input data blocks in bytes. + /// + public int InputBlockSize + { + get + { + return _blockSize; + } + } + + /// + /// Gets the size of the output data blocks in bytes. + /// + public int OutputBlockSize + { + get + { + return _blockSize; + } + } + + /// + /// Gets a value indicating whether multiple blocks can be transformed. + /// + public bool CanTransformMultipleBlocks + { + get + { + return true; + } + } + + /// + /// Gets a value indicating whether the current transform can be reused. + /// + public bool CanReuseTransform + { + get + { + return true; + } + } + + /// + /// Cleanup internal state. + /// + public void Dispose() + { + _encryptor.Dispose(); + } + + #endregion ICryptoTransform Members + } +} diff --git a/ICSharpCode.SharpZipLib/GZip/GZip.cs b/ICSharpCode.SharpZipLib/GZip/GZip.cs new file mode 100644 index 000000000000..e7e4763daea8 --- /dev/null +++ b/ICSharpCode.SharpZipLib/GZip/GZip.cs @@ -0,0 +1,92 @@ +using System; +using System.IO; + +namespace ICSharpCode.SharpZipLib.GZip +{ + using static Zip.Compression.Deflater; + + /// + /// An example class to demonstrate compression and decompression of GZip streams. + /// + public static class GZip + { + /// + /// Decompress the input writing + /// uncompressed data to the output stream + /// + /// The readable stream containing data to decompress. + /// The output stream to receive the decompressed data. + /// Both streams are closed on completion if true. + /// Input or output stream is null + public static void Decompress(Stream inStream, Stream outStream, bool isStreamOwner) + { + if (inStream == null) + throw new ArgumentNullException(nameof(inStream), "Input stream is null"); + + if (outStream == null) + throw new ArgumentNullException(nameof(outStream), "Output stream is null"); + + try + { + using (GZipInputStream gzipInput = new GZipInputStream(inStream)) + { + gzipInput.IsStreamOwner = isStreamOwner; + Core.StreamUtils.Copy(gzipInput, outStream, new byte[4096]); + } + } + finally + { + if (isStreamOwner) + { + // inStream is closed by the GZipInputStream if stream owner + outStream.Dispose(); + } + } + } + + /// + /// Compress the input stream sending + /// result data to output stream + /// + /// The readable stream to compress. + /// The output stream to receive the compressed data. + /// Both streams are closed on completion if true. + /// Deflate buffer size, minimum 512 + /// Deflate compression level, 0-9 + /// Input or output stream is null + /// Buffer Size is smaller than 512 + /// Compression level outside 0-9 + public static void Compress(Stream inStream, Stream outStream, bool isStreamOwner, int bufferSize = 512, int level = 6) + { + if (inStream == null) + throw new ArgumentNullException(nameof(inStream), "Input stream is null"); + + if (outStream == null) + throw new ArgumentNullException(nameof(outStream), "Output stream is null"); + + if (bufferSize < 512) + throw new ArgumentOutOfRangeException(nameof(bufferSize), "Deflate buffer size must be >= 512"); + + if (level < NO_COMPRESSION || level > BEST_COMPRESSION) + throw new ArgumentOutOfRangeException(nameof(level), "Compression level must be 0-9"); + + try + { + using (GZipOutputStream gzipOutput = new GZipOutputStream(outStream, bufferSize)) + { + gzipOutput.SetLevel(level); + gzipOutput.IsStreamOwner = isStreamOwner; + Core.StreamUtils.Copy(inStream, gzipOutput, new byte[bufferSize]); + } + } + finally + { + if (isStreamOwner) + { + // outStream is closed by the GZipOutputStream if stream owner + inStream.Dispose(); + } + } + } + } +} diff --git a/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs b/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs new file mode 100644 index 000000000000..a59799278a99 --- /dev/null +++ b/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs @@ -0,0 +1,78 @@ +using System; +using System.Text; + +namespace ICSharpCode.SharpZipLib.GZip +{ + /// + /// This class contains constants used for gzip. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "kept for backwards compatibility")] + sealed public class GZipConstants + { + /// + /// First GZip identification byte + /// + public const byte ID1 = 0x1F; + + /// + /// Second GZip identification byte + /// + public const byte ID2 = 0x8B; + + /// + /// Deflate compression method + /// + public const byte CompressionMethodDeflate = 0x8; + + /// + /// Get the GZip specified encoding (CP-1252 if supported, otherwise ASCII) + /// + public static Encoding Encoding + { + get + { + try + { + return Encoding.GetEncoding(1252); + } + catch + { + return Encoding.ASCII; + } + } + } + + } + + /// + /// GZip header flags + /// + [Flags] + public enum GZipFlags: byte + { + /// + /// Text flag hinting that the file is in ASCII + /// + FTEXT = 0x1 << 0, + + /// + /// CRC flag indicating that a CRC16 preceeds the data + /// + FHCRC = 0x1 << 1, + + /// + /// Extra flag indicating that extra fields are present + /// + FEXTRA = 0x1 << 2, + + /// + /// Filename flag indicating that the original filename is present + /// + FNAME = 0x1 << 3, + + /// + /// Flag bit mask indicating that a comment is present + /// + FCOMMENT = 0x1 << 4, + } +} diff --git a/ICSharpCode.SharpZipLib/GZip/GZipException.cs b/ICSharpCode.SharpZipLib/GZip/GZipException.cs new file mode 100644 index 000000000000..a0ec6bb51c7a --- /dev/null +++ b/ICSharpCode.SharpZipLib/GZip/GZipException.cs @@ -0,0 +1,54 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib.GZip +{ + /// + /// GZipException represents exceptions specific to GZip classes and code. + /// + [Serializable] + public class GZipException : SharpZipBaseException + { + /// + /// Initialise a new instance of . + /// + public GZipException() + { + } + + /// + /// Initialise a new instance of with its message string. + /// + /// A that describes the error. + public GZipException(string message) + : base(message) + { + } + + /// + /// Initialise a new instance of . + /// + /// A that describes the error. + /// The that caused this exception. + public GZipException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the GZipException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected GZipException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs b/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs new file mode 100644 index 000000000000..20a4ded17f76 --- /dev/null +++ b/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs @@ -0,0 +1,361 @@ +using ICSharpCode.SharpZipLib.Checksum; +using ICSharpCode.SharpZipLib.Zip.Compression; +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; +using System; +using System.IO; +using System.Text; + +namespace ICSharpCode.SharpZipLib.GZip +{ + /// + /// This filter stream is used to decompress a "GZIP" format stream. + /// The "GZIP" format is described baseInputStream RFC 1952. + /// + /// author of the original java version : John Leuner + /// + /// This sample shows how to unzip a gzipped file + /// + /// using System; + /// using System.IO; + /// + /// using ICSharpCode.SharpZipLib.Core; + /// using ICSharpCode.SharpZipLib.GZip; + /// + /// class MainClass + /// { + /// public static void Main(string[] args) + /// { + /// using (Stream inStream = new GZipInputStream(File.OpenRead(args[0]))) + /// using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) { + /// byte[] buffer = new byte[4096]; + /// StreamUtils.Copy(inStream, outStream, buffer); + /// } + /// } + /// } + /// + /// + public class GZipInputStream : InflaterInputStream + { + #region Instance Fields + + /// + /// CRC-32 value for uncompressed data + /// + protected Crc32 crc; + + /// + /// Flag to indicate if we've read the GZIP header yet for the current member (block of compressed data). + /// This is tracked per-block as the file is parsed. + /// + private bool readGZIPHeader; + + /// + /// Flag to indicate if at least one block in a stream with concatenated blocks was read successfully. + /// This allows us to exit gracefully if downstream data is not in gzip format. + /// + private bool completedLastBlock; + + private string fileName; + + #endregion Instance Fields + + #region Constructors + + /// + /// Creates a GZipInputStream with the default buffer size + /// + /// + /// The stream to read compressed data from (baseInputStream GZIP format) + /// + public GZipInputStream(Stream baseInputStream) + : this(baseInputStream, 4096) + { + } + + /// + /// Creates a GZIPInputStream with the specified buffer size + /// + /// + /// The stream to read compressed data from (baseInputStream GZIP format) + /// + /// + /// Size of the buffer to use + /// + public GZipInputStream(Stream baseInputStream, int size) + : base(baseInputStream, new Inflater(true), size) + { + } + + #endregion Constructors + + #region Stream overrides + + /// + /// Reads uncompressed data into an array of bytes + /// + /// + /// The buffer to read uncompressed data into + /// + /// + /// The offset indicating where the data should be placed + /// + /// + /// The number of uncompressed bytes to be read + /// + /// Returns the number of bytes actually read. + public override int Read(byte[] buffer, int offset, int count) + { + // A GZIP file can contain multiple blocks of compressed data, although this is quite rare. + // A compressed block could potentially be empty, so we need to loop until we reach EOF or + // we find data. + while (true) + { + // If we haven't read the header for this block, read it + if (!readGZIPHeader) + { + // Try to read header. If there is no header (0 bytes available), this is EOF. If there is + // an incomplete header, this will throw an exception. + try + { + if (!ReadHeader()) + { + return 0; + } + } + catch (Exception ex) when (completedLastBlock && (ex is GZipException || ex is EndOfStreamException)) + { + // if we completed the last block (i.e. we're in a stream that has multiple blocks concatenated + // we want to return gracefully from any header parsing exceptions since sometimes there may + // be trailing garbage on a stream + return 0; + } + } + + // Try to read compressed data + int bytesRead = base.Read(buffer, offset, count); + if (bytesRead > 0) + { + crc.Update(new ArraySegment(buffer, offset, bytesRead)); + } + + // If this is the end of stream, read the footer + if (inf.IsFinished) + { + ReadFooter(); + } + + // Attempting to read 0 bytes will never yield any bytesRead, so we return instead of looping forever + if (bytesRead > 0 || count == 0) + { + return bytesRead; + } + } + } + + /// + /// Retrieves the filename header field for the block last read + /// + /// + public string GetFilename() + { + return fileName; + } + + #endregion Stream overrides + + #region Support routines + + private bool ReadHeader() + { + // Initialize CRC for this block + crc = new Crc32(); + + // Make sure there is data in file. We can't rely on ReadLeByte() to fill the buffer, as this could be EOF, + // which is fine, but ReadLeByte() throws an exception if it doesn't find data, so we do this part ourselves. + if (inputBuffer.Available <= 0) + { + inputBuffer.Fill(); + if (inputBuffer.Available <= 0) + { + // No header, EOF. + return false; + } + } + + var headCRC = new Crc32(); + + // 1. Check the two magic bytes + + var magic = inputBuffer.ReadLeByte(); + headCRC.Update(magic); + if (magic != GZipConstants.ID1) + { + throw new GZipException("Error GZIP header, first magic byte doesn't match"); + } + + magic = inputBuffer.ReadLeByte(); + if (magic != GZipConstants.ID2) + { + throw new GZipException("Error GZIP header, second magic byte doesn't match"); + } + headCRC.Update(magic); + + // 2. Check the compression type (must be 8) + var compressionType = inputBuffer.ReadLeByte(); + + if (compressionType != GZipConstants.CompressionMethodDeflate) + { + throw new GZipException("Error GZIP header, data not in deflate format"); + } + headCRC.Update(compressionType); + + // 3. Check the flags + var flagsByte = inputBuffer.ReadLeByte(); + + headCRC.Update(flagsByte); + + // 3.1 Check the reserved bits are zero + + if ((flagsByte & 0xE0) != 0) + { + throw new GZipException("Reserved flag bits in GZIP header != 0"); + } + + var flags = (GZipFlags)flagsByte; + + // 4.-6. Skip the modification time, extra flags, and OS type + for (int i = 0; i < 6; i++) + { + headCRC.Update(inputBuffer.ReadLeByte()); + } + + // 7. Read extra field + if (flags.HasFlag(GZipFlags.FEXTRA)) + { + // XLEN is total length of extra subfields, we will skip them all + var len1 = inputBuffer.ReadLeByte(); + var len2 = inputBuffer.ReadLeByte(); + + headCRC.Update(len1); + headCRC.Update(len2); + + int extraLen = (len2 << 8) | len1; // gzip is LSB first + for (int i = 0; i < extraLen; i++) + { + headCRC.Update(inputBuffer.ReadLeByte()); + } + } + + // 8. Read file name + if (flags.HasFlag(GZipFlags.FNAME)) + { + var fname = new byte[1024]; + var fnamePos = 0; + int readByte; + while ((readByte = inputBuffer.ReadLeByte()) > 0) + { + if (fnamePos < 1024) + { + fname[fnamePos++] = (byte)readByte; + } + headCRC.Update(readByte); + } + + headCRC.Update(readByte); + + fileName = GZipConstants.Encoding.GetString(fname, 0, fnamePos); + } + else + { + fileName = null; + } + + // 9. Read comment + if (flags.HasFlag(GZipFlags.FCOMMENT)) + { + int readByte; + while ((readByte = inputBuffer.ReadLeByte()) > 0) + { + headCRC.Update(readByte); + } + + headCRC.Update(readByte); + } + + // 10. Read header CRC + if (flags.HasFlag(GZipFlags.FHCRC)) + { + int tempByte; + int crcval = inputBuffer.ReadLeByte(); + if (crcval < 0) + { + throw new EndOfStreamException("EOS reading GZIP header"); + } + + tempByte = inputBuffer.ReadLeByte(); + if (tempByte < 0) + { + throw new EndOfStreamException("EOS reading GZIP header"); + } + + crcval = (crcval << 8) | tempByte; + if (crcval != ((int)headCRC.Value & 0xffff)) + { + throw new GZipException("Header CRC value mismatch"); + } + } + + readGZIPHeader = true; + return true; + } + + private void ReadFooter() + { + byte[] footer = new byte[8]; + + // End of stream; reclaim all bytes from inf, read the final byte count, and reset the inflator + long bytesRead = inf.TotalOut & 0xffffffff; + inputBuffer.Available += inf.RemainingInput; + inf.Reset(); + + // Read footer from inputBuffer + int needed = 8; + while (needed > 0) + { + int count = inputBuffer.ReadClearTextBuffer(footer, 8 - needed, needed); + if (count <= 0) + { + throw new EndOfStreamException("EOS reading GZIP footer"); + } + needed -= count; // Jewel Jan 16 + } + + // Calculate CRC + int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) | ((footer[2] & 0xff) << 16) | (footer[3] << 24); + if (crcval != (int)crc.Value) + { + throw new GZipException("GZIP crc sum mismatch, theirs \"" + crcval + "\" and ours \"" + (int)crc.Value); + } + + // NOTE The total here is the original total modulo 2 ^ 32. + uint total = + (uint)((uint)footer[4] & 0xff) | + (uint)(((uint)footer[5] & 0xff) << 8) | + (uint)(((uint)footer[6] & 0xff) << 16) | + (uint)((uint)footer[7] << 24); + + if (bytesRead != total) + { + throw new GZipException("Number of bytes mismatch in footer"); + } + + // Mark header read as false so if another header exists, we'll continue reading through the file + readGZIPHeader = false; + + // Indicate that we succeeded on at least one block so we can exit gracefully if there is trailing garbage downstream + completedLastBlock = true; + } + + #endregion Support routines + } +} diff --git a/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs b/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs new file mode 100644 index 000000000000..0b1a647fea53 --- /dev/null +++ b/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs @@ -0,0 +1,293 @@ +using ICSharpCode.SharpZipLib.Checksum; +using ICSharpCode.SharpZipLib.Zip.Compression; +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; +using System; +using System.IO; +using System.Text; + +namespace ICSharpCode.SharpZipLib.GZip +{ + /// + /// This filter stream is used to compress a stream into a "GZIP" stream. + /// The "GZIP" format is described in RFC 1952. + /// + /// author of the original java version : John Leuner + /// + /// This sample shows how to gzip a file + /// + /// using System; + /// using System.IO; + /// + /// using ICSharpCode.SharpZipLib.GZip; + /// using ICSharpCode.SharpZipLib.Core; + /// + /// class MainClass + /// { + /// public static void Main(string[] args) + /// { + /// using (Stream s = new GZipOutputStream(File.Create(args[0] + ".gz"))) + /// using (FileStream fs = File.OpenRead(args[0])) { + /// byte[] writeData = new byte[4096]; + /// Streamutils.Copy(s, fs, writeData); + /// } + /// } + /// } + /// } + /// + /// + public class GZipOutputStream : DeflaterOutputStream + { + private enum OutputState + { + Header, + Footer, + Finished, + Closed, + }; + + #region Instance Fields + + /// + /// CRC-32 value for uncompressed data + /// + protected Crc32 crc = new Crc32(); + + private OutputState state_ = OutputState.Header; + + private string fileName; + + private GZipFlags flags = 0; + + #endregion Instance Fields + + #region Constructors + + /// + /// Creates a GzipOutputStream with the default buffer size + /// + /// + /// The stream to read data (to be compressed) from + /// + public GZipOutputStream(Stream baseOutputStream) + : this(baseOutputStream, 4096) + { + } + + /// + /// Creates a GZipOutputStream with the specified buffer size + /// + /// + /// The stream to read data (to be compressed) from + /// + /// + /// Size of the buffer to use + /// + public GZipOutputStream(Stream baseOutputStream, int size) : base(baseOutputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size) + { + } + + #endregion Constructors + + #region Public API + + /// + /// Sets the active compression level (0-9). The new level will be activated + /// immediately. + /// + /// The compression level to set. + /// + /// Level specified is not supported. + /// + /// + public void SetLevel(int level) + { + if (level < Deflater.NO_COMPRESSION || level > Deflater.BEST_COMPRESSION) + throw new ArgumentOutOfRangeException(nameof(level), "Compression level must be 0-9"); + + deflater_.SetLevel(level); + } + + /// + /// Get the current compression level. + /// + /// The current compression level. + public int GetLevel() + { + return deflater_.GetLevel(); + } + + /// + /// Original filename + /// + public string FileName + { + get => fileName; + set + { + fileName = CleanFilename(value); + if (string.IsNullOrEmpty(fileName)) + { + flags &= ~GZipFlags.FNAME; + } + else + { + flags |= GZipFlags.FNAME; + } + } + } + + #endregion Public API + + #region Stream overrides + + /// + /// Write given buffer to output updating crc + /// + /// Buffer to write + /// Offset of first byte in buf to write + /// Number of bytes to write + public override void Write(byte[] buffer, int offset, int count) + { + if (state_ == OutputState.Header) + { + WriteHeader(); + } + + if (state_ != OutputState.Footer) + { + throw new InvalidOperationException("Write not permitted in current state"); + } + + crc.Update(new ArraySegment(buffer, offset, count)); + base.Write(buffer, offset, count); + } + + /// + /// Writes remaining compressed output data to the output stream + /// and closes it. + /// + protected override void Dispose(bool disposing) + { + try + { + Finish(); + } + finally + { + if (state_ != OutputState.Closed) + { + state_ = OutputState.Closed; + if (IsStreamOwner) + { + baseOutputStream_.Dispose(); + } + } + } + } + + /// + /// Flushes the stream by ensuring the header is written, and then calling Flush + /// on the deflater. + /// + public override void Flush() + { + if (state_ == OutputState.Header) + { + WriteHeader(); + } + + base.Flush(); + } + + #endregion Stream overrides + + #region DeflaterOutputStream overrides + + /// + /// Finish compression and write any footer information required to stream + /// + public override void Finish() + { + // If no data has been written a header should be added. + if (state_ == OutputState.Header) + { + WriteHeader(); + } + + if (state_ == OutputState.Footer) + { + state_ = OutputState.Finished; + base.Finish(); + + var totalin = (uint)(deflater_.TotalIn & 0xffffffff); + var crcval = (uint)(crc.Value & 0xffffffff); + + byte[] gzipFooter; + + unchecked + { + gzipFooter = new byte[] { + (byte) crcval, (byte) (crcval >> 8), + (byte) (crcval >> 16), (byte) (crcval >> 24), + + (byte) totalin, (byte) (totalin >> 8), + (byte) (totalin >> 16), (byte) (totalin >> 24) + }; + } + + baseOutputStream_.Write(gzipFooter, 0, gzipFooter.Length); + } + } + + #endregion DeflaterOutputStream overrides + + #region Support Routines + + private static string CleanFilename(string path) + => path.Substring(path.LastIndexOf('/') + 1); + + private void WriteHeader() + { + if (state_ == OutputState.Header) + { + state_ = OutputState.Footer; + + var mod_time = (int)((DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000L); // Ticks give back 100ns intervals + byte[] gzipHeader = { + // The two magic bytes + GZipConstants.ID1, + GZipConstants.ID2, + + // The compression type + GZipConstants.CompressionMethodDeflate, + + // The flags (not set) + (byte)flags, + + // The modification time + (byte) mod_time, (byte) (mod_time >> 8), + (byte) (mod_time >> 16), (byte) (mod_time >> 24), + + // The extra flags + 0, + + // The OS type (unknown) + 255 + }; + + baseOutputStream_.Write(gzipHeader, 0, gzipHeader.Length); + + if (flags.HasFlag(GZipFlags.FNAME)) + { + var fname = GZipConstants.Encoding.GetBytes(fileName); + baseOutputStream_.Write(fname, 0, fname.Length); + + // End filename string with a \0 + baseOutputStream_.Write(new byte[] { 0 }, 0, 1); + } + } + } + + #endregion Support Routines + } +} diff --git a/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj b/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj new file mode 100644 index 000000000000..066c4fb43346 --- /dev/null +++ b/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj @@ -0,0 +1,44 @@ + + + + netstandard2.0;netstandard2.1;net45 + True + ../../assets/ICSharpCode.SharpZipLib.snk + true + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + + + + 1.3.3 + $(Version).11 + $(FileVersion) + SharpZipLib + ICSharpCode + ICSharpCode + SharpZipLib (#ziplib, formerly NZipLib) is a compression library for Zip, GZip, BZip2, and Tar written entirely in C# for .NET. It is implemented as an assembly (installable in the GAC), and thus can easily be incorporated into other projects (in any .NET language) + MIT + http://icsharpcode.github.io/SharpZipLib/ + images/sharpziplib-nuget-256x256.png + https://github.com/icsharpcode/SharpZipLib + Copyright © 2000-2021 SharpZipLib Contributors + Compression Library Zip GZip BZip2 LZW Tar + en-US + +Please see https://github.com/icsharpcode/SharpZipLib/wiki/Release-1.3.3 for more information. + https://github.com/icsharpcode/SharpZipLib + + + + + + + + + True + images + + + + diff --git a/ICSharpCode.SharpZipLib/Lzw/LzwConstants.cs b/ICSharpCode.SharpZipLib/Lzw/LzwConstants.cs new file mode 100644 index 000000000000..88934830fda3 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Lzw/LzwConstants.cs @@ -0,0 +1,63 @@ +namespace ICSharpCode.SharpZipLib.Lzw +{ + /// + /// This class contains constants used for LZW + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "kept for backwards compatibility")] + sealed public class LzwConstants + { + /// + /// Magic number found at start of LZW header: 0x1f 0x9d + /// + public const int MAGIC = 0x1f9d; + + /// + /// Maximum number of bits per code + /// + public const int MAX_BITS = 16; + + /* 3rd header byte: + * bit 0..4 Number of compression bits + * bit 5 Extended header + * bit 6 Free + * bit 7 Block mode + */ + + /// + /// Mask for 'number of compression bits' + /// + public const int BIT_MASK = 0x1f; + + /// + /// Indicates the presence of a fourth header byte + /// + public const int EXTENDED_MASK = 0x20; + + //public const int FREE_MASK = 0x40; + + /// + /// Reserved bits + /// + public const int RESERVED_MASK = 0x60; + + /// + /// Block compression: if table is full and compression rate is dropping, + /// clear the dictionary. + /// + public const int BLOCK_MODE_MASK = 0x80; + + /// + /// LZW file header size (in bytes) + /// + public const int HDR_SIZE = 3; + + /// + /// Initial number of bits per code + /// + public const int INIT_BITS = 9; + + private LzwConstants() + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Lzw/LzwException.cs b/ICSharpCode.SharpZipLib/Lzw/LzwException.cs new file mode 100644 index 000000000000..1d5c44c3c9b1 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Lzw/LzwException.cs @@ -0,0 +1,54 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib.Lzw +{ + /// + /// LzwException represents exceptions specific to LZW classes and code. + /// + [Serializable] + public class LzwException : SharpZipBaseException + { + /// + /// Initialise a new instance of . + /// + public LzwException() + { + } + + /// + /// Initialise a new instance of with its message string. + /// + /// A that describes the error. + public LzwException(string message) + : base(message) + { + } + + /// + /// Initialise a new instance of . + /// + /// A that describes the error. + /// The that caused this exception. + public LzwException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the LzwException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected LzwException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Lzw/LzwInputStream.cs b/ICSharpCode.SharpZipLib/Lzw/LzwInputStream.cs new file mode 100644 index 000000000000..1045ef77875f --- /dev/null +++ b/ICSharpCode.SharpZipLib/Lzw/LzwInputStream.cs @@ -0,0 +1,572 @@ +using System; +using System.IO; + +namespace ICSharpCode.SharpZipLib.Lzw +{ + /// + /// This filter stream is used to decompress a LZW format stream. + /// Specifically, a stream that uses the LZC compression method. + /// This file format is usually associated with the .Z file extension. + /// + /// See http://en.wikipedia.org/wiki/Compress + /// See http://wiki.wxwidgets.org/Development:_Z_File_Format + /// + /// The file header consists of 3 (or optionally 4) bytes. The first two bytes + /// contain the magic marker "0x1f 0x9d", followed by a byte of flags. + /// + /// Based on Java code by Ronald Tschalar, which in turn was based on the unlzw.c + /// code in the gzip package. + /// + /// This sample shows how to unzip a compressed file + /// + /// using System; + /// using System.IO; + /// + /// using ICSharpCode.SharpZipLib.Core; + /// using ICSharpCode.SharpZipLib.LZW; + /// + /// class MainClass + /// { + /// public static void Main(string[] args) + /// { + /// using (Stream inStream = new LzwInputStream(File.OpenRead(args[0]))) + /// using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) { + /// byte[] buffer = new byte[4096]; + /// StreamUtils.Copy(inStream, outStream, buffer); + /// // OR + /// inStream.Read(buffer, 0, buffer.Length); + /// // now do something with the buffer + /// } + /// } + /// } + /// + /// + public class LzwInputStream : Stream + { + /// + /// Gets or sets a flag indicating ownership of underlying stream. + /// When the flag is true will close the underlying stream also. + /// + /// The default value is true. + public bool IsStreamOwner { get; set; } = true; + + /// + /// Creates a LzwInputStream + /// + /// + /// The stream to read compressed data from (baseInputStream LZW format) + /// + public LzwInputStream(Stream baseInputStream) + { + this.baseInputStream = baseInputStream; + } + + /// + /// See + /// + /// + public override int ReadByte() + { + int b = Read(one, 0, 1); + if (b == 1) + return (one[0] & 0xff); + return -1; + } + + /// + /// Reads decompressed data into the provided buffer byte array + /// + /// + /// The array to read and decompress data into + /// + /// + /// The offset indicating where the data should be placed + /// + /// + /// The number of bytes to decompress + /// + /// The number of bytes read. Zero signals the end of stream + public override int Read(byte[] buffer, int offset, int count) + { + if (!headerParsed) + ParseHeader(); + + if (eof) + return 0; + + int start = offset; + + /* Using local copies of various variables speeds things up by as + * much as 30% in Java! Performance not tested in C#. + */ + int[] lTabPrefix = tabPrefix; + byte[] lTabSuffix = tabSuffix; + byte[] lStack = stack; + int lNBits = nBits; + int lMaxCode = maxCode; + int lMaxMaxCode = maxMaxCode; + int lBitMask = bitMask; + int lOldCode = oldCode; + byte lFinChar = finChar; + int lStackP = stackP; + int lFreeEnt = freeEnt; + byte[] lData = data; + int lBitPos = bitPos; + + // empty stack if stuff still left + int sSize = lStack.Length - lStackP; + if (sSize > 0) + { + int num = (sSize >= count) ? count : sSize; + Array.Copy(lStack, lStackP, buffer, offset, num); + offset += num; + count -= num; + lStackP += num; + } + + if (count == 0) + { + stackP = lStackP; + return offset - start; + } + + // loop, filling local buffer until enough data has been decompressed + MainLoop: + do + { + if (end < EXTRA) + { + Fill(); + } + + int bitIn = (got > 0) ? (end - end % lNBits) << 3 : + (end << 3) - (lNBits - 1); + + while (lBitPos < bitIn) + { + #region A + + // handle 1-byte reads correctly + if (count == 0) + { + nBits = lNBits; + maxCode = lMaxCode; + maxMaxCode = lMaxMaxCode; + bitMask = lBitMask; + oldCode = lOldCode; + finChar = lFinChar; + stackP = lStackP; + freeEnt = lFreeEnt; + bitPos = lBitPos; + + return offset - start; + } + + // check for code-width expansion + if (lFreeEnt > lMaxCode) + { + int nBytes = lNBits << 3; + lBitPos = (lBitPos - 1) + + nBytes - (lBitPos - 1 + nBytes) % nBytes; + + lNBits++; + lMaxCode = (lNBits == maxBits) ? lMaxMaxCode : + (1 << lNBits) - 1; + + lBitMask = (1 << lNBits) - 1; + lBitPos = ResetBuf(lBitPos); + goto MainLoop; + } + + #endregion A + + #region B + + // read next code + int pos = lBitPos >> 3; + int code = (((lData[pos] & 0xFF) | + ((lData[pos + 1] & 0xFF) << 8) | + ((lData[pos + 2] & 0xFF) << 16)) >> + (lBitPos & 0x7)) & lBitMask; + + lBitPos += lNBits; + + // handle first iteration + if (lOldCode == -1) + { + if (code >= 256) + throw new LzwException("corrupt input: " + code + " > 255"); + + lFinChar = (byte)(lOldCode = code); + buffer[offset++] = lFinChar; + count--; + continue; + } + + // handle CLEAR code + if (code == TBL_CLEAR && blockMode) + { + Array.Copy(zeros, 0, lTabPrefix, 0, zeros.Length); + lFreeEnt = TBL_FIRST - 1; + + int nBytes = lNBits << 3; + lBitPos = (lBitPos - 1) + nBytes - (lBitPos - 1 + nBytes) % nBytes; + lNBits = LzwConstants.INIT_BITS; + lMaxCode = (1 << lNBits) - 1; + lBitMask = lMaxCode; + + // Code tables reset + + lBitPos = ResetBuf(lBitPos); + goto MainLoop; + } + + #endregion B + + #region C + + // setup + int inCode = code; + lStackP = lStack.Length; + + // Handle KwK case + if (code >= lFreeEnt) + { + if (code > lFreeEnt) + { + throw new LzwException("corrupt input: code=" + code + + ", freeEnt=" + lFreeEnt); + } + + lStack[--lStackP] = lFinChar; + code = lOldCode; + } + + // Generate output characters in reverse order + while (code >= 256) + { + lStack[--lStackP] = lTabSuffix[code]; + code = lTabPrefix[code]; + } + + lFinChar = lTabSuffix[code]; + buffer[offset++] = lFinChar; + count--; + + // And put them out in forward order + sSize = lStack.Length - lStackP; + int num = (sSize >= count) ? count : sSize; + Array.Copy(lStack, lStackP, buffer, offset, num); + offset += num; + count -= num; + lStackP += num; + + #endregion C + + #region D + + // generate new entry in table + if (lFreeEnt < lMaxMaxCode) + { + lTabPrefix[lFreeEnt] = lOldCode; + lTabSuffix[lFreeEnt] = lFinChar; + lFreeEnt++; + } + + // Remember previous code + lOldCode = inCode; + + // if output buffer full, then return + if (count == 0) + { + nBits = lNBits; + maxCode = lMaxCode; + bitMask = lBitMask; + oldCode = lOldCode; + finChar = lFinChar; + stackP = lStackP; + freeEnt = lFreeEnt; + bitPos = lBitPos; + + return offset - start; + } + + #endregion D + } // while + + lBitPos = ResetBuf(lBitPos); + } while (got > 0); // do..while + + nBits = lNBits; + maxCode = lMaxCode; + bitMask = lBitMask; + oldCode = lOldCode; + finChar = lFinChar; + stackP = lStackP; + freeEnt = lFreeEnt; + bitPos = lBitPos; + + eof = true; + return offset - start; + } + + /// + /// Moves the unread data in the buffer to the beginning and resets + /// the pointers. + /// + /// + /// + private int ResetBuf(int bitPosition) + { + int pos = bitPosition >> 3; + Array.Copy(data, pos, data, 0, end - pos); + end -= pos; + return 0; + } + + private void Fill() + { + got = baseInputStream.Read(data, end, data.Length - 1 - end); + if (got > 0) + { + end += got; + } + } + + private void ParseHeader() + { + headerParsed = true; + + byte[] hdr = new byte[LzwConstants.HDR_SIZE]; + + int result = baseInputStream.Read(hdr, 0, hdr.Length); + + // Check the magic marker + if (result < 0) + throw new LzwException("Failed to read LZW header"); + + if (hdr[0] != (LzwConstants.MAGIC >> 8) || hdr[1] != (LzwConstants.MAGIC & 0xff)) + { + throw new LzwException(String.Format( + "Wrong LZW header. Magic bytes don't match. 0x{0:x2} 0x{1:x2}", + hdr[0], hdr[1])); + } + + // Check the 3rd header byte + blockMode = (hdr[2] & LzwConstants.BLOCK_MODE_MASK) > 0; + maxBits = hdr[2] & LzwConstants.BIT_MASK; + + if (maxBits > LzwConstants.MAX_BITS) + { + throw new LzwException("Stream compressed with " + maxBits + + " bits, but decompression can only handle " + + LzwConstants.MAX_BITS + " bits."); + } + + if ((hdr[2] & LzwConstants.RESERVED_MASK) > 0) + { + throw new LzwException("Unsupported bits set in the header."); + } + + // Initialize variables + maxMaxCode = 1 << maxBits; + nBits = LzwConstants.INIT_BITS; + maxCode = (1 << nBits) - 1; + bitMask = maxCode; + oldCode = -1; + finChar = 0; + freeEnt = blockMode ? TBL_FIRST : 256; + + tabPrefix = new int[1 << maxBits]; + tabSuffix = new byte[1 << maxBits]; + stack = new byte[1 << maxBits]; + stackP = stack.Length; + + for (int idx = 255; idx >= 0; idx--) + tabSuffix[idx] = (byte)idx; + } + + #region Stream Overrides + + /// + /// Gets a value indicating whether the current stream supports reading + /// + public override bool CanRead + { + get + { + return baseInputStream.CanRead; + } + } + + /// + /// Gets a value of false indicating seeking is not supported for this stream. + /// + public override bool CanSeek + { + get + { + return false; + } + } + + /// + /// Gets a value of false indicating that this stream is not writeable. + /// + public override bool CanWrite + { + get + { + return false; + } + } + + /// + /// A value representing the length of the stream in bytes. + /// + public override long Length + { + get + { + return got; + } + } + + /// + /// The current position within the stream. + /// Throws a NotSupportedException when attempting to set the position + /// + /// Attempting to set the position + public override long Position + { + get + { + return baseInputStream.Position; + } + set + { + throw new NotSupportedException("InflaterInputStream Position not supported"); + } + } + + /// + /// Flushes the baseInputStream + /// + public override void Flush() + { + baseInputStream.Flush(); + } + + /// + /// Sets the position within the current stream + /// Always throws a NotSupportedException + /// + /// The relative offset to seek to. + /// The defining where to seek from. + /// The new position in the stream. + /// Any access + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("Seek not supported"); + } + + /// + /// Set the length of the current stream + /// Always throws a NotSupportedException + /// + /// The new length value for the stream. + /// Any access + public override void SetLength(long value) + { + throw new NotSupportedException("InflaterInputStream SetLength not supported"); + } + + /// + /// Writes a sequence of bytes to stream and advances the current position + /// This method always throws a NotSupportedException + /// + /// The buffer containing data to write. + /// The offset of the first byte to write. + /// The number of bytes to write. + /// Any access + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException("InflaterInputStream Write not supported"); + } + + /// + /// Writes one byte to the current stream and advances the current position + /// Always throws a NotSupportedException + /// + /// The byte to write. + /// Any access + public override void WriteByte(byte value) + { + throw new NotSupportedException("InflaterInputStream WriteByte not supported"); + } + + /// + /// Closes the input stream. When + /// is true the underlying stream is also closed. + /// + protected override void Dispose(bool disposing) + { + if (!isClosed) + { + isClosed = true; + if (IsStreamOwner) + { + baseInputStream.Dispose(); + } + } + } + + #endregion Stream Overrides + + #region Instance Fields + + private Stream baseInputStream; + + /// + /// Flag indicating wether this instance has been closed or not. + /// + private bool isClosed; + + private readonly byte[] one = new byte[1]; + private bool headerParsed; + + // string table stuff + private const int TBL_CLEAR = 0x100; + + private const int TBL_FIRST = TBL_CLEAR + 1; + + private int[] tabPrefix; + private byte[] tabSuffix; + private readonly int[] zeros = new int[256]; + private byte[] stack; + + // various state + private bool blockMode; + + private int nBits; + private int maxBits; + private int maxMaxCode; + private int maxCode; + private int bitMask; + private int oldCode; + private byte finChar; + private int stackP; + private int freeEnt; + + // input buffer + private readonly byte[] data = new byte[1024 * 8]; + + private int bitPos; + private int end; + private int got; + private bool eof; + private const int EXTRA = 64; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Tar/InvalidHeaderException.cs b/ICSharpCode.SharpZipLib/Tar/InvalidHeaderException.cs new file mode 100644 index 000000000000..9f385e425ab0 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Tar/InvalidHeaderException.cs @@ -0,0 +1,55 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib.Tar +{ + /// + /// This exception is used to indicate that there is a problem + /// with a TAR archive header. + /// + [Serializable] + public class InvalidHeaderException : TarException + { + /// + /// Initialise a new instance of the InvalidHeaderException class. + /// + public InvalidHeaderException() + { + } + + /// + /// Initialises a new instance of the InvalidHeaderException class with a specified message. + /// + /// Message describing the exception cause. + public InvalidHeaderException(string message) + : base(message) + { + } + + /// + /// Initialise a new instance of InvalidHeaderException + /// + /// Message describing the problem. + /// The exception that is the cause of the current exception. + public InvalidHeaderException(string message, Exception exception) + : base(message, exception) + { + } + + /// + /// Initializes a new instance of the InvalidHeaderException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected InvalidHeaderException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Tar/TarArchive.cs b/ICSharpCode.SharpZipLib/Tar/TarArchive.cs new file mode 100644 index 000000000000..6db6b23b9a73 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Tar/TarArchive.cs @@ -0,0 +1,1028 @@ +using System; +using System.IO; +using System.Numerics; +using System.Text; +using ICSharpCode.SharpZipLib.Core; + +namespace ICSharpCode.SharpZipLib.Tar +{ + /// + /// Used to advise clients of 'events' while processing archives + /// + public delegate void ProgressMessageHandler(TarArchive archive, TarEntry entry, string message); + + /// + /// The TarArchive class implements the concept of a + /// 'Tape Archive'. A tar archive is a series of entries, each of + /// which represents a file system object. Each entry in + /// the archive consists of a header block followed by 0 or more data blocks. + /// Directory entries consist only of the header block, and are followed by entries + /// for the directory's contents. File entries consist of a + /// header followed by the number of blocks needed to + /// contain the file's contents. All entries are written on + /// block boundaries. Blocks are 512 bytes long. + /// + /// TarArchives are instantiated in either read or write mode, + /// based upon whether they are instantiated with an InputStream + /// or an OutputStream. Once instantiated TarArchives read/write + /// mode can not be changed. + /// + /// There is currently no support for random access to tar archives. + /// However, it seems that subclassing TarArchive, and using the + /// TarBuffer.CurrentRecord and TarBuffer.CurrentBlock + /// properties, this would be rather trivial. + /// + public class TarArchive : IDisposable + { + /// + /// Client hook allowing detailed information to be reported during processing + /// + public event ProgressMessageHandler ProgressMessageEvent; + + /// + /// Raises the ProgressMessage event + /// + /// The TarEntry for this event + /// message for this event. Null is no message + protected virtual void OnProgressMessageEvent(TarEntry entry, string message) + { + ProgressMessageHandler handler = ProgressMessageEvent; + if (handler != null) + { + handler(this, entry, message); + } + } + + #region Constructors + + /// + /// Constructor for a default . + /// + protected TarArchive() + { + } + + /// + /// Initialise a TarArchive for input. + /// + /// The to use for input. + protected TarArchive(TarInputStream stream) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + tarIn = stream; + } + + /// + /// Initialise a TarArchive for output. + /// + /// The to use for output. + protected TarArchive(TarOutputStream stream) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + tarOut = stream; + } + + #endregion Constructors + + #region Static factory methods + + /// + /// The InputStream based constructors create a TarArchive for the + /// purposes of extracting or listing a tar archive. Thus, use + /// these constructors when you wish to extract files from or list + /// the contents of an existing tar archive. + /// + /// The stream to retrieve archive data from. + /// Returns a new suitable for reading from. + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public static TarArchive CreateInputTarArchive(Stream inputStream) + { + return CreateInputTarArchive(inputStream, null); + } + + /// + /// The InputStream based constructors create a TarArchive for the + /// purposes of extracting or listing a tar archive. Thus, use + /// these constructors when you wish to extract files from or list + /// the contents of an existing tar archive. + /// + /// The stream to retrieve archive data from. + /// The used for the Name fields, or null for ASCII only + /// Returns a new suitable for reading from. + public static TarArchive CreateInputTarArchive(Stream inputStream, Encoding nameEncoding) + { + if (inputStream == null) + { + throw new ArgumentNullException(nameof(inputStream)); + } + + var tarStream = inputStream as TarInputStream; + + TarArchive result; + if (tarStream != null) + { + result = new TarArchive(tarStream); + } + else + { + result = CreateInputTarArchive(inputStream, TarBuffer.DefaultBlockFactor, nameEncoding); + } + return result; + } + + /// + /// Create TarArchive for reading setting block factor + /// + /// A stream containing the tar archive contents + /// The blocking factor to apply + /// Returns a suitable for reading. + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public static TarArchive CreateInputTarArchive(Stream inputStream, int blockFactor) + { + return CreateInputTarArchive(inputStream, blockFactor, null); + } + + /// + /// Create TarArchive for reading setting block factor + /// + /// A stream containing the tar archive contents + /// The blocking factor to apply + /// The used for the Name fields, or null for ASCII only + /// Returns a suitable for reading. + public static TarArchive CreateInputTarArchive(Stream inputStream, int blockFactor, Encoding nameEncoding) + { + if (inputStream == null) + { + throw new ArgumentNullException(nameof(inputStream)); + } + + if (inputStream is TarInputStream) + { + throw new ArgumentException("TarInputStream not valid"); + } + + return new TarArchive(new TarInputStream(inputStream, blockFactor, nameEncoding)); + } + /// + /// Create a TarArchive for writing to, using the default blocking factor + /// + /// The to write to + /// The used for the Name fields, or null for ASCII only + /// Returns a suitable for writing. + public static TarArchive CreateOutputTarArchive(Stream outputStream, Encoding nameEncoding) + { + if (outputStream == null) + { + throw new ArgumentNullException(nameof(outputStream)); + } + + var tarStream = outputStream as TarOutputStream; + + TarArchive result; + if (tarStream != null) + { + result = new TarArchive(tarStream); + } + else + { + result = CreateOutputTarArchive(outputStream, TarBuffer.DefaultBlockFactor, nameEncoding); + } + return result; + } + /// + /// Create a TarArchive for writing to, using the default blocking factor + /// + /// The to write to + /// Returns a suitable for writing. + public static TarArchive CreateOutputTarArchive(Stream outputStream) + { + return CreateOutputTarArchive(outputStream, null); + } + + /// + /// Create a tar archive for writing. + /// + /// The stream to write to + /// The blocking factor to use for buffering. + /// Returns a suitable for writing. + public static TarArchive CreateOutputTarArchive(Stream outputStream, int blockFactor) + { + return CreateOutputTarArchive(outputStream, blockFactor, null); + } + /// + /// Create a tar archive for writing. + /// + /// The stream to write to + /// The blocking factor to use for buffering. + /// The used for the Name fields, or null for ASCII only + /// Returns a suitable for writing. + public static TarArchive CreateOutputTarArchive(Stream outputStream, int blockFactor, Encoding nameEncoding) + { + if (outputStream == null) + { + throw new ArgumentNullException(nameof(outputStream)); + } + + if (outputStream is TarOutputStream) + { + throw new ArgumentException("TarOutputStream is not valid"); + } + + return new TarArchive(new TarOutputStream(outputStream, blockFactor, nameEncoding)); + } + + #endregion Static factory methods + + /// + /// Set the flag that determines whether existing files are + /// kept, or overwritten during extraction. + /// + /// + /// If true, do not overwrite existing files. + /// + public void SetKeepOldFiles(bool keepExistingFiles) + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + keepOldFiles = keepExistingFiles; + } + + /// + /// Get/set the ascii file translation flag. If ascii file translation + /// is true, then the file is checked to see if it a binary file or not. + /// If the flag is true and the test indicates it is ascii text + /// file, it will be translated. The translation converts the local + /// operating system's concept of line ends into the UNIX line end, + /// '\n', which is the defacto standard for a TAR archive. This makes + /// text files compatible with UNIX. + /// + public bool AsciiTranslate + { + get + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + return asciiTranslate; + } + + set + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + asciiTranslate = value; + } + } + + /// + /// Set the ascii file translation flag. + /// + /// + /// If true, translate ascii text files. + /// + [Obsolete("Use the AsciiTranslate property")] + public void SetAsciiTranslation(bool translateAsciiFiles) + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + asciiTranslate = translateAsciiFiles; + } + + /// + /// PathPrefix is added to entry names as they are written if the value is not null. + /// A slash character is appended after PathPrefix + /// + public string PathPrefix + { + get + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + return pathPrefix; + } + + set + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + pathPrefix = value; + } + } + + /// + /// RootPath is removed from entry names if it is found at the + /// beginning of the name. + /// + public string RootPath + { + get + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + return rootPath; + } + + set + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + // Convert to forward slashes for matching. Trim trailing / for correct final path + rootPath = value.Replace('\\', '/').TrimEnd('/'); + } + } + + /// + /// Set user and group information that will be used to fill in the + /// tar archive's entry headers. This information is based on that available + /// for the linux operating system, which is not always available on other + /// operating systems. TarArchive allows the programmer to specify values + /// to be used in their place. + /// is set to true by this call. + /// + /// + /// The user id to use in the headers. + /// + /// + /// The user name to use in the headers. + /// + /// + /// The group id to use in the headers. + /// + /// + /// The group name to use in the headers. + /// + public void SetUserInfo(int userId, string userName, int groupId, string groupName) + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + this.userId = userId; + this.userName = userName; + this.groupId = groupId; + this.groupName = groupName; + applyUserInfoOverrides = true; + } + + /// + /// Get or set a value indicating if overrides defined by SetUserInfo should be applied. + /// + /// If overrides are not applied then the values as set in each header will be used. + public bool ApplyUserInfoOverrides + { + get + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + return applyUserInfoOverrides; + } + + set + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + applyUserInfoOverrides = value; + } + } + + /// + /// Get the archive user id. + /// See ApplyUserInfoOverrides for detail + /// on how to allow setting values on a per entry basis. + /// + /// + /// The current user id. + /// + public int UserId + { + get + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + return userId; + } + } + + /// + /// Get the archive user name. + /// See ApplyUserInfoOverrides for detail + /// on how to allow setting values on a per entry basis. + /// + /// + /// The current user name. + /// + public string UserName + { + get + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + return userName; + } + } + + /// + /// Get the archive group id. + /// See ApplyUserInfoOverrides for detail + /// on how to allow setting values on a per entry basis. + /// + /// + /// The current group id. + /// + public int GroupId + { + get + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + return groupId; + } + } + + /// + /// Get the archive group name. + /// See ApplyUserInfoOverrides for detail + /// on how to allow setting values on a per entry basis. + /// + /// + /// The current group name. + /// + public string GroupName + { + get + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + return groupName; + } + } + + /// + /// Get the archive's record size. Tar archives are composed of + /// a series of RECORDS each containing a number of BLOCKS. + /// This allowed tar archives to match the IO characteristics of + /// the physical device being used. Archives are expected + /// to be properly "blocked". + /// + /// + /// The record size this archive is using. + /// + public int RecordSize + { + get + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + if (tarIn != null) + { + return tarIn.RecordSize; + } + else if (tarOut != null) + { + return tarOut.RecordSize; + } + return TarBuffer.DefaultRecordSize; + } + } + + /// + /// Sets the IsStreamOwner property on the underlying stream. + /// Set this to false to prevent the Close of the TarArchive from closing the stream. + /// + public bool IsStreamOwner + { + set + { + if (tarIn != null) + { + tarIn.IsStreamOwner = value; + } + else + { + tarOut.IsStreamOwner = value; + } + } + } + + /// + /// Close the archive. + /// + [Obsolete("Use Close instead")] + public void CloseArchive() + { + Close(); + } + + /// + /// Perform the "list" command for the archive contents. + /// + /// NOTE That this method uses the progress event to actually list + /// the contents. If the progress display event is not set, nothing will be listed! + /// + public void ListContents() + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + while (true) + { + TarEntry entry = tarIn.GetNextEntry(); + + if (entry == null) + { + break; + } + OnProgressMessageEvent(entry, null); + } + } + + /// + /// Perform the "extract" command and extract the contents of the archive. + /// + /// + /// The destination directory into which to extract. + /// + public void ExtractContents(string destinationDirectory) + => ExtractContents(destinationDirectory, false); + + /// + /// Perform the "extract" command and extract the contents of the archive. + /// + /// + /// The destination directory into which to extract. + /// + /// Allow parent directory traversal in file paths (e.g. ../file) + public void ExtractContents(string destinationDirectory, bool allowParentTraversal) + { + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + var fullDistDir = Path.GetFullPath(destinationDirectory).TrimEnd('/', '\\'); + + while (true) + { + TarEntry entry = tarIn.GetNextEntry(); + + if (entry == null) + { + break; + } + + if (entry.TarHeader.TypeFlag == TarHeader.LF_LINK || entry.TarHeader.TypeFlag == TarHeader.LF_SYMLINK) + continue; + + ExtractEntry(fullDistDir, entry, allowParentTraversal); + } + } + + /// + /// Extract an entry from the archive. This method assumes that the + /// tarIn stream has been properly set with a call to GetNextEntry(). + /// + /// + /// The destination directory into which to extract. + /// + /// + /// The TarEntry returned by tarIn.GetNextEntry(). + /// + /// Allow parent directory traversal in file paths (e.g. ../file) + private void ExtractEntry(string destDir, TarEntry entry, bool allowParentTraversal) + { + OnProgressMessageEvent(entry, null); + + string name = entry.Name; + + if (Path.IsPathRooted(name)) + { + // NOTE: + // for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt + name = name.Substring(Path.GetPathRoot(name).Length); + } + + name = name.Replace('/', Path.DirectorySeparatorChar); + + string destFile = Path.Combine(destDir, name); + var destFileDir = Path.GetDirectoryName(Path.GetFullPath(destFile)) ?? ""; + + if (!allowParentTraversal && !destFileDir.StartsWith(destDir, StringComparison.InvariantCultureIgnoreCase)) + { + throw new InvalidNameException("Parent traversal in paths is not allowed"); + } + + if (entry.IsDirectory) + { + EnsureDirectoryExists(destFile); + } + else + { + string parentDirectory = Path.GetDirectoryName(destFile); + EnsureDirectoryExists(parentDirectory); + + bool process = true; + var fileInfo = new FileInfo(destFile); + if (fileInfo.Exists) + { + if (keepOldFiles) + { + OnProgressMessageEvent(entry, "Destination file already exists"); + process = false; + } + else if ((fileInfo.Attributes & FileAttributes.ReadOnly) != 0) + { + OnProgressMessageEvent(entry, "Destination file already exists, and is read-only"); + process = false; + } + } + + if (process) + { + using (var outputStream = File.Create(destFile)) + { + if (this.asciiTranslate) + { + // May need to translate the file. + ExtractAndTranslateEntry(destFile, outputStream); + } + else + { + // If translation is disabled, just copy the entry across directly. + tarIn.CopyEntryContents(outputStream); + } + } + } + } + } + + // Extract a TAR entry, and perform an ASCII translation if required. + private void ExtractAndTranslateEntry(string destFile, Stream outputStream) + { + bool asciiTrans = !IsBinary(destFile); + + if (asciiTrans) + { + using (var outw = new StreamWriter(outputStream, new UTF8Encoding(false), 1024, true)) + { + byte[] rdbuf = new byte[32 * 1024]; + + while (true) + { + int numRead = tarIn.Read(rdbuf, 0, rdbuf.Length); + + if (numRead <= 0) + { + break; + } + + for (int off = 0, b = 0; b < numRead; ++b) + { + if (rdbuf[b] == 10) + { + string s = Encoding.ASCII.GetString(rdbuf, off, (b - off)); + outw.WriteLine(s); + off = b + 1; + } + } + } + } + } + else + { + // No translation required. + tarIn.CopyEntryContents(outputStream); + } + } + + /// + /// Write an entry to the archive. This method will call the putNextEntry + /// and then write the contents of the entry, and finally call closeEntry() + /// for entries that are files. For directories, it will call putNextEntry(), + /// and then, if the recurse flag is true, process each entry that is a + /// child of the directory. + /// + /// + /// The TarEntry representing the entry to write to the archive. + /// + /// + /// If true, process the children of directory entries. + /// + public void WriteEntry(TarEntry sourceEntry, bool recurse) + { + if (sourceEntry == null) + { + throw new ArgumentNullException(nameof(sourceEntry)); + } + + if (isDisposed) + { + throw new ObjectDisposedException("TarArchive"); + } + + try + { + if (recurse) + { + TarHeader.SetValueDefaults(sourceEntry.UserId, sourceEntry.UserName, + sourceEntry.GroupId, sourceEntry.GroupName); + } + WriteEntryCore(sourceEntry, recurse); + } + finally + { + if (recurse) + { + TarHeader.RestoreSetValues(); + } + } + } + + /// + /// Write an entry to the archive. This method will call the putNextEntry + /// and then write the contents of the entry, and finally call closeEntry() + /// for entries that are files. For directories, it will call putNextEntry(), + /// and then, if the recurse flag is true, process each entry that is a + /// child of the directory. + /// + /// + /// The TarEntry representing the entry to write to the archive. + /// + /// + /// If true, process the children of directory entries. + /// + private void WriteEntryCore(TarEntry sourceEntry, bool recurse) + { + string tempFileName = null; + string entryFilename = sourceEntry.File; + + var entry = (TarEntry)sourceEntry.Clone(); + + if (applyUserInfoOverrides) + { + entry.GroupId = groupId; + entry.GroupName = groupName; + entry.UserId = userId; + entry.UserName = userName; + } + + OnProgressMessageEvent(entry, null); + + if (asciiTranslate && !entry.IsDirectory) + { + if (!IsBinary(entryFilename)) + { + tempFileName = PathUtils.GetTempFileName(); + + using (StreamReader inStream = File.OpenText(entryFilename)) + { + using (Stream outStream = File.Create(tempFileName)) + { + while (true) + { + string line = inStream.ReadLine(); + if (line == null) + { + break; + } + byte[] data = Encoding.ASCII.GetBytes(line); + outStream.Write(data, 0, data.Length); + outStream.WriteByte((byte)'\n'); + } + + outStream.Flush(); + } + } + + entry.Size = new FileInfo(tempFileName).Length; + entryFilename = tempFileName; + } + } + + string newName = null; + + if (!String.IsNullOrEmpty(rootPath)) + { + if (entry.Name.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) + { + newName = entry.Name.Substring(rootPath.Length + 1); + } + } + + if (pathPrefix != null) + { + newName = (newName == null) ? pathPrefix + "/" + entry.Name : pathPrefix + "/" + newName; + } + + if (newName != null) + { + entry.Name = newName; + } + + tarOut.PutNextEntry(entry); + + if (entry.IsDirectory) + { + if (recurse) + { + TarEntry[] list = entry.GetDirectoryEntries(); + for (int i = 0; i < list.Length; ++i) + { + WriteEntryCore(list[i], recurse); + } + } + } + else + { + using (Stream inputStream = File.OpenRead(entryFilename)) + { + byte[] localBuffer = new byte[32 * 1024]; + while (true) + { + int numRead = inputStream.Read(localBuffer, 0, localBuffer.Length); + + if (numRead <= 0) + { + break; + } + + tarOut.Write(localBuffer, 0, numRead); + } + } + + if (!string.IsNullOrEmpty(tempFileName)) + { + File.Delete(tempFileName); + } + + tarOut.CloseEntry(); + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases the unmanaged resources used by the FileStream and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; + /// false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!isDisposed) + { + isDisposed = true; + if (disposing) + { + if (tarOut != null) + { + tarOut.Flush(); + tarOut.Dispose(); + } + + if (tarIn != null) + { + tarIn.Dispose(); + } + } + } + } + + /// + /// Closes the archive and releases any associated resources. + /// + public virtual void Close() + { + Dispose(true); + } + + /// + /// Ensures that resources are freed and other cleanup operations are performed + /// when the garbage collector reclaims the . + /// + ~TarArchive() + { + Dispose(false); + } + + private static void EnsureDirectoryExists(string directoryName) + { + if (!Directory.Exists(directoryName)) + { + try + { + Directory.CreateDirectory(directoryName); + } + catch (Exception e) + { + throw new TarException("Exception creating directory '" + directoryName + "', " + e.Message, e); + } + } + } + + // TODO: TarArchive - Is there a better way to test for a text file? + // It no longer reads entire files into memory but is still a weak test! + // This assumes that byte values 0-7, 14-31 or 255 are binary + // and that all non text files contain one of these values + private static bool IsBinary(string filename) + { + using (FileStream fs = File.OpenRead(filename)) + { + int sampleSize = Math.Min(4096, (int)fs.Length); + byte[] content = new byte[sampleSize]; + + int bytesRead = fs.Read(content, 0, sampleSize); + + for (int i = 0; i < bytesRead; ++i) + { + byte b = content[i]; + if ((b < 8) || ((b > 13) && (b < 32)) || (b == 255)) + { + return true; + } + } + } + return false; + } + + #region Instance Fields + + private bool keepOldFiles; + private bool asciiTranslate; + + private int userId; + private string userName = string.Empty; + private int groupId; + private string groupName = string.Empty; + + private string rootPath; + private string pathPrefix; + + private bool applyUserInfoOverrides; + + private TarInputStream tarIn; + private TarOutputStream tarOut; + private bool isDisposed; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Tar/TarBuffer.cs b/ICSharpCode.SharpZipLib/Tar/TarBuffer.cs new file mode 100644 index 000000000000..744c13189a3f --- /dev/null +++ b/ICSharpCode.SharpZipLib/Tar/TarBuffer.cs @@ -0,0 +1,599 @@ +using System; +using System.IO; + +namespace ICSharpCode.SharpZipLib.Tar +{ + /// + /// The TarBuffer class implements the tar archive concept + /// of a buffered input stream. This concept goes back to the + /// days of blocked tape drives and special io devices. In the + /// C# universe, the only real function that this class + /// performs is to ensure that files have the correct "record" + /// size, or other tars will complain. + ///

+ /// You should never have a need to access this class directly. + /// TarBuffers are created by Tar IO Streams. + ///

+ ///
+ public class TarBuffer + { + /* A quote from GNU tar man file on blocking and records + A `tar' archive file contains a series of blocks. Each block + contains `BLOCKSIZE' bytes. Although this format may be thought of as + being on magnetic tape, other media are often used. + + Each file archived is represented by a header block which describes + the file, followed by zero or more blocks which give the contents of + the file. At the end of the archive file there may be a block filled + with binary zeros as an end-of-file marker. A reasonable system should + write a block of zeros at the end, but must not assume that such a + block exists when reading an archive. + + The blocks may be "blocked" for physical I/O operations. Each + record of N blocks is written with a single 'write ()' + operation. On magnetic tapes, the result of such a write is a single + record. When writing an archive, the last record of blocks should be + written at the full size, with blocks after the zero block containing + all zeros. When reading an archive, a reasonable system should + properly handle an archive whose last record is shorter than the rest, + or which contains garbage records after a zero block. + */ + + #region Constants + + /// + /// The size of a block in a tar archive in bytes. + /// + /// This is 512 bytes. + public const int BlockSize = 512; + + /// + /// The number of blocks in a default record. + /// + /// + /// The default value is 20 blocks per record. + /// + public const int DefaultBlockFactor = 20; + + /// + /// The size in bytes of a default record. + /// + /// + /// The default size is 10KB. + /// + public const int DefaultRecordSize = BlockSize * DefaultBlockFactor; + + #endregion Constants + + /// + /// Get the record size for this buffer + /// + /// The record size in bytes. + /// This is equal to the multiplied by the + public int RecordSize + { + get + { + return recordSize; + } + } + + /// + /// Get the TAR Buffer's record size. + /// + /// The record size in bytes. + /// This is equal to the multiplied by the + [Obsolete("Use RecordSize property instead")] + public int GetRecordSize() + { + return recordSize; + } + + /// + /// Get the Blocking factor for the buffer + /// + /// This is the number of blocks in each record. + public int BlockFactor + { + get + { + return blockFactor; + } + } + + /// + /// Get the TAR Buffer's block factor + /// + /// The block factor; the number of blocks per record. + [Obsolete("Use BlockFactor property instead")] + public int GetBlockFactor() + { + return blockFactor; + } + + /// + /// Construct a default TarBuffer + /// + protected TarBuffer() + { + } + + /// + /// Create TarBuffer for reading with default BlockFactor + /// + /// Stream to buffer + /// A new suitable for input. + public static TarBuffer CreateInputTarBuffer(Stream inputStream) + { + if (inputStream == null) + { + throw new ArgumentNullException(nameof(inputStream)); + } + + return CreateInputTarBuffer(inputStream, DefaultBlockFactor); + } + + /// + /// Construct TarBuffer for reading inputStream setting BlockFactor + /// + /// Stream to buffer + /// Blocking factor to apply + /// A new suitable for input. + public static TarBuffer CreateInputTarBuffer(Stream inputStream, int blockFactor) + { + if (inputStream == null) + { + throw new ArgumentNullException(nameof(inputStream)); + } + + if (blockFactor <= 0) + { + throw new ArgumentOutOfRangeException(nameof(blockFactor), "Factor cannot be negative"); + } + + var tarBuffer = new TarBuffer(); + tarBuffer.inputStream = inputStream; + tarBuffer.outputStream = null; + tarBuffer.Initialize(blockFactor); + + return tarBuffer; + } + + /// + /// Construct TarBuffer for writing with default BlockFactor + /// + /// output stream for buffer + /// A new suitable for output. + public static TarBuffer CreateOutputTarBuffer(Stream outputStream) + { + if (outputStream == null) + { + throw new ArgumentNullException(nameof(outputStream)); + } + + return CreateOutputTarBuffer(outputStream, DefaultBlockFactor); + } + + /// + /// Construct TarBuffer for writing Tar output to streams. + /// + /// Output stream to write to. + /// Blocking factor to apply + /// A new suitable for output. + public static TarBuffer CreateOutputTarBuffer(Stream outputStream, int blockFactor) + { + if (outputStream == null) + { + throw new ArgumentNullException(nameof(outputStream)); + } + + if (blockFactor <= 0) + { + throw new ArgumentOutOfRangeException(nameof(blockFactor), "Factor cannot be negative"); + } + + var tarBuffer = new TarBuffer(); + tarBuffer.inputStream = null; + tarBuffer.outputStream = outputStream; + tarBuffer.Initialize(blockFactor); + + return tarBuffer; + } + + /// + /// Initialization common to all constructors. + /// + private void Initialize(int archiveBlockFactor) + { + blockFactor = archiveBlockFactor; + recordSize = archiveBlockFactor * BlockSize; + recordBuffer = new byte[RecordSize]; + + if (inputStream != null) + { + currentRecordIndex = -1; + currentBlockIndex = BlockFactor; + } + else + { + currentRecordIndex = 0; + currentBlockIndex = 0; + } + } + + /// + /// Determine if an archive block indicates End of Archive. End of + /// archive is indicated by a block that consists entirely of null bytes. + /// All remaining blocks for the record should also be null's + /// However some older tars only do a couple of null blocks (Old GNU tar for one) + /// and also partial records + /// + /// The data block to check. + /// Returns true if the block is an EOF block; false otherwise. + [Obsolete("Use IsEndOfArchiveBlock instead")] + public bool IsEOFBlock(byte[] block) + { + if (block == null) + { + throw new ArgumentNullException(nameof(block)); + } + + if (block.Length != BlockSize) + { + throw new ArgumentException("block length is invalid"); + } + + for (int i = 0; i < BlockSize; ++i) + { + if (block[i] != 0) + { + return false; + } + } + + return true; + } + + /// + /// Determine if an archive block indicates the End of an Archive has been reached. + /// End of archive is indicated by a block that consists entirely of null bytes. + /// All remaining blocks for the record should also be null's + /// However some older tars only do a couple of null blocks (Old GNU tar for one) + /// and also partial records + /// + /// The data block to check. + /// Returns true if the block is an EOF block; false otherwise. + public static bool IsEndOfArchiveBlock(byte[] block) + { + if (block == null) + { + throw new ArgumentNullException(nameof(block)); + } + + if (block.Length != BlockSize) + { + throw new ArgumentException("block length is invalid"); + } + + for (int i = 0; i < BlockSize; ++i) + { + if (block[i] != 0) + { + return false; + } + } + + return true; + } + + /// + /// Skip over a block on the input stream. + /// + public void SkipBlock() + { + if (inputStream == null) + { + throw new TarException("no input stream defined"); + } + + if (currentBlockIndex >= BlockFactor) + { + if (!ReadRecord()) + { + throw new TarException("Failed to read a record"); + } + } + + currentBlockIndex++; + } + + /// + /// Read a block from the input stream. + /// + /// + /// The block of data read. + /// + public byte[] ReadBlock() + { + if (inputStream == null) + { + throw new TarException("TarBuffer.ReadBlock - no input stream defined"); + } + + if (currentBlockIndex >= BlockFactor) + { + if (!ReadRecord()) + { + throw new TarException("Failed to read a record"); + } + } + + byte[] result = new byte[BlockSize]; + + Array.Copy(recordBuffer, (currentBlockIndex * BlockSize), result, 0, BlockSize); + currentBlockIndex++; + return result; + } + + /// + /// Read a record from data stream. + /// + /// + /// false if End-Of-File, else true. + /// + private bool ReadRecord() + { + if (inputStream == null) + { + throw new TarException("no input stream defined"); + } + + currentBlockIndex = 0; + + int offset = 0; + int bytesNeeded = RecordSize; + + while (bytesNeeded > 0) + { + long numBytes = inputStream.Read(recordBuffer, offset, bytesNeeded); + + // + // NOTE + // We have found EOF, and the record is not full! + // + // This is a broken archive. It does not follow the standard + // blocking algorithm. However, because we are generous, and + // it requires little effort, we will simply ignore the error + // and continue as if the entire record were read. This does + // not appear to break anything upstream. We used to return + // false in this case. + // + // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix. + // + if (numBytes <= 0) + { + break; + } + + offset += (int)numBytes; + bytesNeeded -= (int)numBytes; + } + + currentRecordIndex++; + return true; + } + + /// + /// Get the current block number, within the current record, zero based. + /// + /// Block numbers are zero based values + /// + public int CurrentBlock + { + get { return currentBlockIndex; } + } + + /// + /// Gets or sets a flag indicating ownership of underlying stream. + /// When the flag is true will close the underlying stream also. + /// + /// The default value is true. + public bool IsStreamOwner { get; set; } = true; + + /// + /// Get the current block number, within the current record, zero based. + /// + /// + /// The current zero based block number. + /// + /// + /// The absolute block number = (record number * block factor) + block number. + /// + [Obsolete("Use CurrentBlock property instead")] + public int GetCurrentBlockNum() + { + return currentBlockIndex; + } + + /// + /// Get the current record number. + /// + /// + /// The current zero based record number. + /// + public int CurrentRecord + { + get { return currentRecordIndex; } + } + + /// + /// Get the current record number. + /// + /// + /// The current zero based record number. + /// + [Obsolete("Use CurrentRecord property instead")] + public int GetCurrentRecordNum() + { + return currentRecordIndex; + } + + /// + /// Write a block of data to the archive. + /// + /// + /// The data to write to the archive. + /// + public void WriteBlock(byte[] block) + { + if (block == null) + { + throw new ArgumentNullException(nameof(block)); + } + + if (outputStream == null) + { + throw new TarException("TarBuffer.WriteBlock - no output stream defined"); + } + + if (block.Length != BlockSize) + { + string errorText = string.Format("TarBuffer.WriteBlock - block to write has length '{0}' which is not the block size of '{1}'", + block.Length, BlockSize); + throw new TarException(errorText); + } + + if (currentBlockIndex >= BlockFactor) + { + WriteRecord(); + } + + Array.Copy(block, 0, recordBuffer, (currentBlockIndex * BlockSize), BlockSize); + currentBlockIndex++; + } + + /// + /// Write an archive record to the archive, where the record may be + /// inside of a larger array buffer. The buffer must be "offset plus + /// record size" long. + /// + /// + /// The buffer containing the record data to write. + /// + /// + /// The offset of the record data within buffer. + /// + public void WriteBlock(byte[] buffer, int offset) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (outputStream == null) + { + throw new TarException("TarBuffer.WriteBlock - no output stream defined"); + } + + if ((offset < 0) || (offset >= buffer.Length)) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + if ((offset + BlockSize) > buffer.Length) + { + string errorText = string.Format("TarBuffer.WriteBlock - record has length '{0}' with offset '{1}' which is less than the record size of '{2}'", + buffer.Length, offset, recordSize); + throw new TarException(errorText); + } + + if (currentBlockIndex >= BlockFactor) + { + WriteRecord(); + } + + Array.Copy(buffer, offset, recordBuffer, (currentBlockIndex * BlockSize), BlockSize); + + currentBlockIndex++; + } + + /// + /// Write a TarBuffer record to the archive. + /// + private void WriteRecord() + { + if (outputStream == null) + { + throw new TarException("TarBuffer.WriteRecord no output stream defined"); + } + + outputStream.Write(recordBuffer, 0, RecordSize); + outputStream.Flush(); + + currentBlockIndex = 0; + currentRecordIndex++; + } + + /// + /// WriteFinalRecord writes the current record buffer to output any unwritten data is present. + /// + /// Any trailing bytes are set to zero which is by definition correct behaviour + /// for the end of a tar stream. + private void WriteFinalRecord() + { + if (outputStream == null) + { + throw new TarException("TarBuffer.WriteFinalRecord no output stream defined"); + } + + if (currentBlockIndex > 0) + { + int dataBytes = currentBlockIndex * BlockSize; + Array.Clear(recordBuffer, dataBytes, RecordSize - dataBytes); + WriteRecord(); + } + + outputStream.Flush(); + } + + /// + /// Close the TarBuffer. If this is an output buffer, also flush the + /// current block before closing. + /// + public void Close() + { + if (outputStream != null) + { + WriteFinalRecord(); + + if (IsStreamOwner) + { + outputStream.Dispose(); + } + outputStream = null; + } + else if (inputStream != null) + { + if (IsStreamOwner) + { + inputStream.Dispose(); + } + inputStream = null; + } + } + + #region Instance Fields + + private Stream inputStream; + private Stream outputStream; + + private byte[] recordBuffer; + private int currentBlockIndex; + private int currentRecordIndex; + + private int recordSize = DefaultRecordSize; + private int blockFactor = DefaultBlockFactor; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Tar/TarEntry.cs b/ICSharpCode.SharpZipLib/Tar/TarEntry.cs new file mode 100644 index 000000000000..262c12ad34b2 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Tar/TarEntry.cs @@ -0,0 +1,598 @@ +using System; +using System.IO; +using System.Text; +using ICSharpCode.SharpZipLib.Core; + +namespace ICSharpCode.SharpZipLib.Tar +{ + /// + /// This class represents an entry in a Tar archive. It consists + /// of the entry's header, as well as the entry's File. Entries + /// can be instantiated in one of three ways, depending on how + /// they are to be used. + ///

+ /// TarEntries that are created from the header bytes read from + /// an archive are instantiated with the TarEntry( byte[] ) + /// constructor. These entries will be used when extracting from + /// or listing the contents of an archive. These entries have their + /// header filled in using the header bytes. They also set the File + /// to null, since they reference an archive entry not a file.

+ ///

+ /// TarEntries that are created from files that are to be written + /// into an archive are instantiated with the CreateEntryFromFile(string) + /// pseudo constructor. These entries have their header filled in using + /// the File's information. They also keep a reference to the File + /// for convenience when writing entries.

+ ///

+ /// Finally, TarEntries can be constructed from nothing but a name. + /// This allows the programmer to construct the entry by hand, for + /// instance when only an InputStream is available for writing to + /// the archive, and the header information is constructed from + /// other information. In this case the header fields are set to + /// defaults and the File is set to null.

+ /// + ///
+ public class TarEntry + { + #region Constructors + + /// + /// Initialise a default instance of . + /// + private TarEntry() + { + header = new TarHeader(); + } + + /// + /// Construct an entry from an archive's header bytes. File is set + /// to null. + /// + /// + /// The header bytes from a tar archive entry. + /// + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public TarEntry(byte[] headerBuffer) : this(headerBuffer, null) + { + } + + /// + /// Construct an entry from an archive's header bytes. File is set + /// to null. + /// + /// + /// The header bytes from a tar archive entry. + /// + /// + /// The used for the Name fields, or null for ASCII only + /// + public TarEntry(byte[] headerBuffer, Encoding nameEncoding) + { + header = new TarHeader(); + header.ParseBuffer(headerBuffer, nameEncoding); + } + + /// + /// Construct a TarEntry using the header provided + /// + /// Header details for entry + public TarEntry(TarHeader header) + { + if (header == null) + { + throw new ArgumentNullException(nameof(header)); + } + + this.header = (TarHeader)header.Clone(); + } + + #endregion Constructors + + #region ICloneable Members + + /// + /// Clone this tar entry. + /// + /// Returns a clone of this entry. + public object Clone() + { + var entry = new TarEntry(); + entry.file = file; + entry.header = (TarHeader)header.Clone(); + entry.Name = Name; + return entry; + } + + #endregion ICloneable Members + + /// + /// Construct an entry with only a name. + /// This allows the programmer to construct the entry's header "by hand". + /// + /// The name to use for the entry + /// Returns the newly created + public static TarEntry CreateTarEntry(string name) + { + var entry = new TarEntry(); + TarEntry.NameTarHeader(entry.header, name); + return entry; + } + + /// + /// Construct an entry for a file. File is set to file, and the + /// header is constructed from information from the file. + /// + /// The file name that the entry represents. + /// Returns the newly created + public static TarEntry CreateEntryFromFile(string fileName) + { + var entry = new TarEntry(); + entry.GetFileTarHeader(entry.header, fileName); + return entry; + } + + /// + /// Determine if the two entries are equal. Equality is determined + /// by the header names being equal. + /// + /// The to compare with the current Object. + /// + /// True if the entries are equal; false if not. + /// + public override bool Equals(object obj) + { + var localEntry = obj as TarEntry; + + if (localEntry != null) + { + return Name.Equals(localEntry.Name); + } + return false; + } + + /// + /// Derive a Hash value for the current + /// + /// A Hash code for the current + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + /// + /// Determine if the given entry is a descendant of this entry. + /// Descendancy is determined by the name of the descendant + /// starting with this entry's name. + /// + /// + /// Entry to be checked as a descendent of this. + /// + /// + /// True if entry is a descendant of this. + /// + public bool IsDescendent(TarEntry toTest) + { + if (toTest == null) + { + throw new ArgumentNullException(nameof(toTest)); + } + + return toTest.Name.StartsWith(Name, StringComparison.Ordinal); + } + + /// + /// Get this entry's header. + /// + /// + /// This entry's TarHeader. + /// + public TarHeader TarHeader + { + get + { + return header; + } + } + + /// + /// Get/Set this entry's name. + /// + public string Name + { + get + { + return header.Name; + } + set + { + header.Name = value; + } + } + + /// + /// Get/set this entry's user id. + /// + public int UserId + { + get + { + return header.UserId; + } + set + { + header.UserId = value; + } + } + + /// + /// Get/set this entry's group id. + /// + public int GroupId + { + get + { + return header.GroupId; + } + set + { + header.GroupId = value; + } + } + + /// + /// Get/set this entry's user name. + /// + public string UserName + { + get + { + return header.UserName; + } + set + { + header.UserName = value; + } + } + + /// + /// Get/set this entry's group name. + /// + public string GroupName + { + get + { + return header.GroupName; + } + set + { + header.GroupName = value; + } + } + + /// + /// Convenience method to set this entry's group and user ids. + /// + /// + /// This entry's new user id. + /// + /// + /// This entry's new group id. + /// + public void SetIds(int userId, int groupId) + { + UserId = userId; + GroupId = groupId; + } + + /// + /// Convenience method to set this entry's group and user names. + /// + /// + /// This entry's new user name. + /// + /// + /// This entry's new group name. + /// + public void SetNames(string userName, string groupName) + { + UserName = userName; + GroupName = groupName; + } + + /// + /// Get/Set the modification time for this entry + /// + public DateTime ModTime + { + get + { + return header.ModTime; + } + set + { + header.ModTime = value; + } + } + + /// + /// Get this entry's file. + /// + /// + /// This entry's file. + /// + public string File + { + get + { + return file; + } + } + + /// + /// Get/set this entry's recorded file size. + /// + public long Size + { + get + { + return header.Size; + } + set + { + header.Size = value; + } + } + + /// + /// Return true if this entry represents a directory, false otherwise + /// + /// + /// True if this entry is a directory. + /// + public bool IsDirectory + { + get + { + if (file != null) + { + return Directory.Exists(file); + } + + if (header != null) + { + if ((header.TypeFlag == TarHeader.LF_DIR) || Name.EndsWith("/", StringComparison.Ordinal)) + { + return true; + } + } + return false; + } + } + + /// + /// Fill in a TarHeader with information from a File. + /// + /// + /// The TarHeader to fill in. + /// + /// + /// The file from which to get the header information. + /// + public void GetFileTarHeader(TarHeader header, string file) + { + if (header == null) + { + throw new ArgumentNullException(nameof(header)); + } + + if (file == null) + { + throw new ArgumentNullException(nameof(file)); + } + + this.file = file; + + // bugfix from torhovl from #D forum: + string name = file; + + // 23-Jan-2004 GnuTar allows device names in path where the name is not local to the current directory + if (name.IndexOf(Directory.GetCurrentDirectory(), StringComparison.Ordinal) == 0) + { + name = name.Substring(Directory.GetCurrentDirectory().Length); + } + + /* + if (Path.DirectorySeparatorChar == '\\') + { + // check if the OS is Windows + // Strip off drive letters! + if (name.Length > 2) + { + char ch1 = name[0]; + char ch2 = name[1]; + + if (ch2 == ':' && Char.IsLetter(ch1)) + { + name = name.Substring(2); + } + } + } + */ + + name = name.Replace(Path.DirectorySeparatorChar, '/'); + + // No absolute pathnames + // Windows (and Posix?) paths can start with UNC style "\\NetworkDrive\", + // so we loop on starting /'s. + while (name.StartsWith("/", StringComparison.Ordinal)) + { + name = name.Substring(1); + } + + header.LinkName = String.Empty; + header.Name = name; + + if (Directory.Exists(file)) + { + header.Mode = 1003; // Magic number for security access for a UNIX filesystem + header.TypeFlag = TarHeader.LF_DIR; + if ((header.Name.Length == 0) || header.Name[header.Name.Length - 1] != '/') + { + header.Name = header.Name + "/"; + } + + header.Size = 0; + } + else + { + header.Mode = 33216; // Magic number for security access for a UNIX filesystem + header.TypeFlag = TarHeader.LF_NORMAL; + header.Size = new FileInfo(file.Replace('/', Path.DirectorySeparatorChar)).Length; + } + + header.ModTime = System.IO.File.GetLastWriteTime(file.Replace('/', Path.DirectorySeparatorChar)).ToUniversalTime(); + header.DevMajor = 0; + header.DevMinor = 0; + } + + /// + /// Get entries for all files present in this entries directory. + /// If this entry doesnt represent a directory zero entries are returned. + /// + /// + /// An array of TarEntry's for this entry's children. + /// + public TarEntry[] GetDirectoryEntries() + { + if ((file == null) || !Directory.Exists(file)) + { + return Empty.Array(); + } + + string[] list = Directory.GetFileSystemEntries(file); + TarEntry[] result = new TarEntry[list.Length]; + + for (int i = 0; i < list.Length; ++i) + { + result[i] = TarEntry.CreateEntryFromFile(list[i]); + } + + return result; + } + + /// + /// Write an entry's header information to a header buffer. + /// + /// + /// The tar entry header buffer to fill in. + /// + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public void WriteEntryHeader(byte[] outBuffer) + { + WriteEntryHeader(outBuffer, null); + } + + /// + /// Write an entry's header information to a header buffer. + /// + /// + /// The tar entry header buffer to fill in. + /// + /// + /// The used for the Name fields, or null for ASCII only + /// + public void WriteEntryHeader(byte[] outBuffer, Encoding nameEncoding) + { + header.WriteHeader(outBuffer, nameEncoding); + } + + /// + /// Convenience method that will modify an entry's name directly + /// in place in an entry header buffer byte array. + /// + /// + /// The buffer containing the entry header to modify. + /// + /// + /// The new name to place into the header buffer. + /// + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + static public void AdjustEntryName(byte[] buffer, string newName) + { + AdjustEntryName(buffer, newName, null); + } + + /// + /// Convenience method that will modify an entry's name directly + /// in place in an entry header buffer byte array. + /// + /// + /// The buffer containing the entry header to modify. + /// + /// + /// The new name to place into the header buffer. + /// + /// + /// The used for the Name fields, or null for ASCII only + /// + static public void AdjustEntryName(byte[] buffer, string newName, Encoding nameEncoding) + { + TarHeader.GetNameBytes(newName, buffer, 0, TarHeader.NAMELEN, nameEncoding); + } + + /// + /// Fill in a TarHeader given only the entry's name. + /// + /// + /// The TarHeader to fill in. + /// + /// + /// The tar entry name. + /// + static public void NameTarHeader(TarHeader header, string name) + { + if (header == null) + { + throw new ArgumentNullException(nameof(header)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + bool isDir = name.EndsWith("/", StringComparison.Ordinal); + + header.Name = name; + header.Mode = isDir ? 1003 : 33216; + header.UserId = 0; + header.GroupId = 0; + header.Size = 0; + + header.ModTime = DateTime.UtcNow; + + header.TypeFlag = isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL; + + header.LinkName = String.Empty; + header.UserName = String.Empty; + header.GroupName = String.Empty; + + header.DevMajor = 0; + header.DevMinor = 0; + } + + #region Instance Fields + + /// + /// The name of the file this entry represents or null if the entry is not based on a file. + /// + private string file; + + /// + /// The entry's header information. + /// + private TarHeader header; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Tar/TarException.cs b/ICSharpCode.SharpZipLib/Tar/TarException.cs new file mode 100644 index 000000000000..9d448ca7d636 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Tar/TarException.cs @@ -0,0 +1,54 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib.Tar +{ + /// + /// TarException represents exceptions specific to Tar classes and code. + /// + [Serializable] + public class TarException : SharpZipBaseException + { + /// + /// Initialise a new instance of . + /// + public TarException() + { + } + + /// + /// Initialise a new instance of with its message string. + /// + /// A that describes the error. + public TarException(string message) + : base(message) + { + } + + /// + /// Initialise a new instance of . + /// + /// A that describes the error. + /// The that caused this exception. + public TarException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the TarException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected TarException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Tar/TarExtendedHeaderReader.cs b/ICSharpCode.SharpZipLib/Tar/TarExtendedHeaderReader.cs new file mode 100644 index 000000000000..d1d438ad070e --- /dev/null +++ b/ICSharpCode.SharpZipLib/Tar/TarExtendedHeaderReader.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.SharpZipLib.Tar +{ + /// + /// Reads the extended header of a Tar stream + /// + public class TarExtendedHeaderReader + { + private const byte LENGTH = 0; + private const byte KEY = 1; + private const byte VALUE = 2; + private const byte END = 3; + + private readonly Dictionary headers = new Dictionary(); + + private string[] headerParts = new string[3]; + + private int bbIndex; + private byte[] byteBuffer; + private char[] charBuffer; + + private readonly StringBuilder sb = new StringBuilder(); + private readonly Decoder decoder = Encoding.UTF8.GetDecoder(); + + private int state = LENGTH; + + private static readonly byte[] StateNext = new[] { (byte)' ', (byte)'=', (byte)'\n' }; + + /// + /// Creates a new . + /// + public TarExtendedHeaderReader() + { + ResetBuffers(); + } + + /// + /// Read bytes from + /// + /// + /// + public void Read(byte[] buffer, int length) + { + for (int i = 0; i < length; i++) + { + byte next = buffer[i]; + + if (next == StateNext[state]) + { + Flush(); + headerParts[state] = sb.ToString(); + sb.Clear(); + + if (++state == END) + { + headers.Add(headerParts[KEY], headerParts[VALUE]); + headerParts = new string[3]; + state = LENGTH; + } + } + else + { + byteBuffer[bbIndex++] = next; + if (bbIndex == 4) + Flush(); + } + } + } + + private void Flush() + { + decoder.Convert(byteBuffer, 0, bbIndex, charBuffer, 0, 4, false, out int bytesUsed, out int charsUsed, out bool completed); + + sb.Append(charBuffer, 0, charsUsed); + ResetBuffers(); + } + + private void ResetBuffers() + { + charBuffer = new char[4]; + byteBuffer = new byte[4]; + bbIndex = 0; + } + + /// + /// Returns the parsed headers as key-value strings + /// + public Dictionary Headers + { + get + { + // TODO: Check for invalid state? -NM 2018-07-01 + return headers; + } + } + } +} diff --git a/ICSharpCode.SharpZipLib/Tar/TarHeader.cs b/ICSharpCode.SharpZipLib/Tar/TarHeader.cs new file mode 100644 index 000000000000..3bd1bdffe5d8 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Tar/TarHeader.cs @@ -0,0 +1,1310 @@ +using System; +using System.Text; + +namespace ICSharpCode.SharpZipLib.Tar +{ + /// + /// This class encapsulates the Tar Entry Header used in Tar Archives. + /// The class also holds a number of tar constants, used mostly in headers. + /// + /// + /// The tar format and its POSIX successor PAX have a long history which makes for compatability + /// issues when creating and reading files. + /// + /// This is further complicated by a large number of programs with variations on formats + /// One common issue is the handling of names longer than 100 characters. + /// GNU style long names are currently supported. + /// + /// This is the ustar (Posix 1003.1) header. + /// + /// struct header + /// { + /// char t_name[100]; // 0 Filename + /// char t_mode[8]; // 100 Permissions + /// char t_uid[8]; // 108 Numerical User ID + /// char t_gid[8]; // 116 Numerical Group ID + /// char t_size[12]; // 124 Filesize + /// char t_mtime[12]; // 136 st_mtime + /// char t_chksum[8]; // 148 Checksum + /// char t_typeflag; // 156 Type of File + /// char t_linkname[100]; // 157 Target of Links + /// char t_magic[6]; // 257 "ustar" or other... + /// char t_version[2]; // 263 Version fixed to 00 + /// char t_uname[32]; // 265 User Name + /// char t_gname[32]; // 297 Group Name + /// char t_devmajor[8]; // 329 Major for devices + /// char t_devminor[8]; // 337 Minor for devices + /// char t_prefix[155]; // 345 Prefix for t_name + /// char t_mfill[12]; // 500 Filler up to 512 + /// }; + /// + public class TarHeader + { + #region Constants + + /// + /// The length of the name field in a header buffer. + /// + public const int NAMELEN = 100; + + /// + /// The length of the mode field in a header buffer. + /// + public const int MODELEN = 8; + + /// + /// The length of the user id field in a header buffer. + /// + public const int UIDLEN = 8; + + /// + /// The length of the group id field in a header buffer. + /// + public const int GIDLEN = 8; + + /// + /// The length of the checksum field in a header buffer. + /// + public const int CHKSUMLEN = 8; + + /// + /// Offset of checksum in a header buffer. + /// + public const int CHKSUMOFS = 148; + + /// + /// The length of the size field in a header buffer. + /// + public const int SIZELEN = 12; + + /// + /// The length of the magic field in a header buffer. + /// + public const int MAGICLEN = 6; + + /// + /// The length of the version field in a header buffer. + /// + public const int VERSIONLEN = 2; + + /// + /// The length of the modification time field in a header buffer. + /// + public const int MODTIMELEN = 12; + + /// + /// The length of the user name field in a header buffer. + /// + public const int UNAMELEN = 32; + + /// + /// The length of the group name field in a header buffer. + /// + public const int GNAMELEN = 32; + + /// + /// The length of the devices field in a header buffer. + /// + public const int DEVLEN = 8; + + /// + /// The length of the name prefix field in a header buffer. + /// + public const int PREFIXLEN = 155; + + // + // LF_ constants represent the "type" of an entry + // + + /// + /// The "old way" of indicating a normal file. + /// + public const byte LF_OLDNORM = 0; + + /// + /// Normal file type. + /// + public const byte LF_NORMAL = (byte)'0'; + + /// + /// Link file type. + /// + public const byte LF_LINK = (byte)'1'; + + /// + /// Symbolic link file type. + /// + public const byte LF_SYMLINK = (byte)'2'; + + /// + /// Character device file type. + /// + public const byte LF_CHR = (byte)'3'; + + /// + /// Block device file type. + /// + public const byte LF_BLK = (byte)'4'; + + /// + /// Directory file type. + /// + public const byte LF_DIR = (byte)'5'; + + /// + /// FIFO (pipe) file type. + /// + public const byte LF_FIFO = (byte)'6'; + + /// + /// Contiguous file type. + /// + public const byte LF_CONTIG = (byte)'7'; + + /// + /// Posix.1 2001 global extended header + /// + public const byte LF_GHDR = (byte)'g'; + + /// + /// Posix.1 2001 extended header + /// + public const byte LF_XHDR = (byte)'x'; + + // POSIX allows for upper case ascii type as extensions + + /// + /// Solaris access control list file type + /// + public const byte LF_ACL = (byte)'A'; + + /// + /// GNU dir dump file type + /// This is a dir entry that contains the names of files that were in the + /// dir at the time the dump was made + /// + public const byte LF_GNU_DUMPDIR = (byte)'D'; + + /// + /// Solaris Extended Attribute File + /// + public const byte LF_EXTATTR = (byte)'E'; + + /// + /// Inode (metadata only) no file content + /// + public const byte LF_META = (byte)'I'; + + /// + /// Identifies the next file on the tape as having a long link name + /// + public const byte LF_GNU_LONGLINK = (byte)'K'; + + /// + /// Identifies the next file on the tape as having a long name + /// + public const byte LF_GNU_LONGNAME = (byte)'L'; + + /// + /// Continuation of a file that began on another volume + /// + public const byte LF_GNU_MULTIVOL = (byte)'M'; + + /// + /// For storing filenames that dont fit in the main header (old GNU) + /// + public const byte LF_GNU_NAMES = (byte)'N'; + + /// + /// GNU Sparse file + /// + public const byte LF_GNU_SPARSE = (byte)'S'; + + /// + /// GNU Tape/volume header ignore on extraction + /// + public const byte LF_GNU_VOLHDR = (byte)'V'; + + /// + /// The magic tag representing a POSIX tar archive. (would be written with a trailing NULL) + /// + public const string TMAGIC = "ustar"; + + /// + /// The magic tag representing an old GNU tar archive where version is included in magic and overwrites it + /// + public const string GNU_TMAGIC = "ustar "; + + private const long timeConversionFactor = 10000000L; // 1 tick == 100 nanoseconds + private static readonly DateTime dateTime1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0); + + #endregion Constants + + #region Constructors + + /// + /// Initialise a default TarHeader instance + /// + public TarHeader() + { + Magic = TMAGIC; + Version = " "; + + Name = ""; + LinkName = ""; + + UserId = defaultUserId; + GroupId = defaultGroupId; + UserName = defaultUser; + GroupName = defaultGroupName; + Size = 0; + } + + #endregion Constructors + + #region Properties + + /// + /// Get/set the name for this tar entry. + /// + /// Thrown when attempting to set the property to null. + public string Name + { + get { return name; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + name = value; + } + } + + /// + /// Get the name of this entry. + /// + /// The entry's name. + [Obsolete("Use the Name property instead", true)] + public string GetName() + { + return name; + } + + /// + /// Get/set the entry's Unix style permission mode. + /// + public int Mode + { + get { return mode; } + set { mode = value; } + } + + /// + /// The entry's user id. + /// + /// + /// This is only directly relevant to unix systems. + /// The default is zero. + /// + public int UserId + { + get { return userId; } + set { userId = value; } + } + + /// + /// Get/set the entry's group id. + /// + /// + /// This is only directly relevant to linux/unix systems. + /// The default value is zero. + /// + public int GroupId + { + get { return groupId; } + set { groupId = value; } + } + + /// + /// Get/set the entry's size. + /// + /// Thrown when setting the size to less than zero. + public long Size + { + get { return size; } + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "Cannot be less than zero"); + } + size = value; + } + } + + /// + /// Get/set the entry's modification time. + /// + /// + /// The modification time is only accurate to within a second. + /// + /// Thrown when setting the date time to less than 1/1/1970. + public DateTime ModTime + { + get { return modTime; } + set + { + if (value < dateTime1970) + { + throw new ArgumentOutOfRangeException(nameof(value), "ModTime cannot be before Jan 1st 1970"); + } + modTime = new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second); + } + } + + /// + /// Get the entry's checksum. This is only valid/updated after writing or reading an entry. + /// + public int Checksum + { + get { return checksum; } + } + + /// + /// Get value of true if the header checksum is valid, false otherwise. + /// + public bool IsChecksumValid + { + get { return isChecksumValid; } + } + + /// + /// Get/set the entry's type flag. + /// + public byte TypeFlag + { + get { return typeFlag; } + set { typeFlag = value; } + } + + /// + /// The entry's link name. + /// + /// Thrown when attempting to set LinkName to null. + public string LinkName + { + get { return linkName; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + linkName = value; + } + } + + /// + /// Get/set the entry's magic tag. + /// + /// Thrown when attempting to set Magic to null. + public string Magic + { + get { return magic; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + magic = value; + } + } + + /// + /// The entry's version. + /// + /// Thrown when attempting to set Version to null. + public string Version + { + get + { + return version; + } + + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + version = value; + } + } + + /// + /// The entry's user name. + /// + public string UserName + { + get { return userName; } + set + { + if (value != null) + { + userName = value.Substring(0, Math.Min(UNAMELEN, value.Length)); + } + else + { + string currentUser = "user"; + if (currentUser.Length > UNAMELEN) + { + currentUser = currentUser.Substring(0, UNAMELEN); + } + userName = currentUser; + } + } + } + + /// + /// Get/set the entry's group name. + /// + /// + /// This is only directly relevant to unix systems. + /// + public string GroupName + { + get { return groupName; } + set + { + if (value == null) + { + groupName = "None"; + } + else + { + groupName = value; + } + } + } + + /// + /// Get/set the entry's major device number. + /// + public int DevMajor + { + get { return devMajor; } + set { devMajor = value; } + } + + /// + /// Get/set the entry's minor device number. + /// + public int DevMinor + { + get { return devMinor; } + set { devMinor = value; } + } + + #endregion Properties + + #region ICloneable Members + + /// + /// Create a new that is a copy of the current instance. + /// + /// A new that is a copy of the current instance. + public object Clone() + { + return this.MemberwiseClone(); + } + + #endregion ICloneable Members + + /// + /// Parse TarHeader information from a header buffer. + /// + /// + /// The tar entry header buffer to get information from. + /// + /// + /// The used for the Name field, or null for ASCII only + /// + public void ParseBuffer(byte[] header, Encoding nameEncoding) + { + if (header == null) + { + throw new ArgumentNullException(nameof(header)); + } + + int offset = 0; + + name = ParseName(header, offset, NAMELEN, nameEncoding).ToString(); + offset += NAMELEN; + + mode = (int)ParseOctal(header, offset, MODELEN); + offset += MODELEN; + + UserId = (int)ParseOctal(header, offset, UIDLEN); + offset += UIDLEN; + + GroupId = (int)ParseOctal(header, offset, GIDLEN); + offset += GIDLEN; + + Size = ParseBinaryOrOctal(header, offset, SIZELEN); + offset += SIZELEN; + + ModTime = GetDateTimeFromCTime(ParseOctal(header, offset, MODTIMELEN)); + offset += MODTIMELEN; + + checksum = (int)ParseOctal(header, offset, CHKSUMLEN); + offset += CHKSUMLEN; + + TypeFlag = header[offset++]; + + LinkName = ParseName(header, offset, NAMELEN, nameEncoding).ToString(); + offset += NAMELEN; + + Magic = ParseName(header, offset, MAGICLEN, nameEncoding).ToString(); + offset += MAGICLEN; + + if (Magic == "ustar") + { + Version = ParseName(header, offset, VERSIONLEN, nameEncoding).ToString(); + offset += VERSIONLEN; + + UserName = ParseName(header, offset, UNAMELEN, nameEncoding).ToString(); + offset += UNAMELEN; + + GroupName = ParseName(header, offset, GNAMELEN, nameEncoding).ToString(); + offset += GNAMELEN; + + DevMajor = (int)ParseOctal(header, offset, DEVLEN); + offset += DEVLEN; + + DevMinor = (int)ParseOctal(header, offset, DEVLEN); + offset += DEVLEN; + + string prefix = ParseName(header, offset, PREFIXLEN, nameEncoding).ToString(); + if (!string.IsNullOrEmpty(prefix)) Name = prefix + '/' + Name; + } + + isChecksumValid = Checksum == TarHeader.MakeCheckSum(header); + } + + /// + /// Parse TarHeader information from a header buffer. + /// + /// + /// The tar entry header buffer to get information from. + /// + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public void ParseBuffer(byte[] header) + { + ParseBuffer(header, null); + } + + /// + /// 'Write' header information to buffer provided, updating the check sum. + /// + /// output buffer for header information + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public void WriteHeader(byte[] outBuffer) + { + WriteHeader(outBuffer, null); + } + + /// + /// 'Write' header information to buffer provided, updating the check sum. + /// + /// output buffer for header information + /// The used for the Name field, or null for ASCII only + public void WriteHeader(byte[] outBuffer, Encoding nameEncoding) + { + if (outBuffer == null) + { + throw new ArgumentNullException(nameof(outBuffer)); + } + + int offset = 0; + + offset = GetNameBytes(Name, outBuffer, offset, NAMELEN, nameEncoding); + offset = GetOctalBytes(mode, outBuffer, offset, MODELEN); + offset = GetOctalBytes(UserId, outBuffer, offset, UIDLEN); + offset = GetOctalBytes(GroupId, outBuffer, offset, GIDLEN); + + offset = GetBinaryOrOctalBytes(Size, outBuffer, offset, SIZELEN); + offset = GetOctalBytes(GetCTime(ModTime), outBuffer, offset, MODTIMELEN); + + int csOffset = offset; + for (int c = 0; c < CHKSUMLEN; ++c) + { + outBuffer[offset++] = (byte)' '; + } + + outBuffer[offset++] = TypeFlag; + + offset = GetNameBytes(LinkName, outBuffer, offset, NAMELEN, nameEncoding); + offset = GetAsciiBytes(Magic, 0, outBuffer, offset, MAGICLEN, nameEncoding); + offset = GetNameBytes(Version, outBuffer, offset, VERSIONLEN, nameEncoding); + offset = GetNameBytes(UserName, outBuffer, offset, UNAMELEN, nameEncoding); + offset = GetNameBytes(GroupName, outBuffer, offset, GNAMELEN, nameEncoding); + + if ((TypeFlag == LF_CHR) || (TypeFlag == LF_BLK)) + { + offset = GetOctalBytes(DevMajor, outBuffer, offset, DEVLEN); + offset = GetOctalBytes(DevMinor, outBuffer, offset, DEVLEN); + } + + for (; offset < outBuffer.Length;) + { + outBuffer[offset++] = 0; + } + + checksum = ComputeCheckSum(outBuffer); + + GetCheckSumOctalBytes(checksum, outBuffer, csOffset, CHKSUMLEN); + isChecksumValid = true; + } + + /// + /// Get a hash code for the current object. + /// + /// A hash code for the current object. + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + /// + /// Determines if this instance is equal to the specified object. + /// + /// The object to compare with. + /// true if the objects are equal, false otherwise. + public override bool Equals(object obj) + { + var localHeader = obj as TarHeader; + + bool result; + if (localHeader != null) + { + result = (name == localHeader.name) + && (mode == localHeader.mode) + && (UserId == localHeader.UserId) + && (GroupId == localHeader.GroupId) + && (Size == localHeader.Size) + && (ModTime == localHeader.ModTime) + && (Checksum == localHeader.Checksum) + && (TypeFlag == localHeader.TypeFlag) + && (LinkName == localHeader.LinkName) + && (Magic == localHeader.Magic) + && (Version == localHeader.Version) + && (UserName == localHeader.UserName) + && (GroupName == localHeader.GroupName) + && (DevMajor == localHeader.DevMajor) + && (DevMinor == localHeader.DevMinor); + } + else + { + result = false; + } + return result; + } + + /// + /// Set defaults for values used when constructing a TarHeader instance. + /// + /// Value to apply as a default for userId. + /// Value to apply as a default for userName. + /// Value to apply as a default for groupId. + /// Value to apply as a default for groupName. + static internal void SetValueDefaults(int userId, string userName, int groupId, string groupName) + { + defaultUserId = userIdAsSet = userId; + defaultUser = userNameAsSet = userName; + defaultGroupId = groupIdAsSet = groupId; + defaultGroupName = groupNameAsSet = groupName; + } + + static internal void RestoreSetValues() + { + defaultUserId = userIdAsSet; + defaultUser = userNameAsSet; + defaultGroupId = groupIdAsSet; + defaultGroupName = groupNameAsSet; + } + + // Return value that may be stored in octal or binary. Length must exceed 8. + // + static private long ParseBinaryOrOctal(byte[] header, int offset, int length) + { + if (header[offset] >= 0x80) + { + // File sizes over 8GB are stored in 8 right-justified bytes of binary indicated by setting the high-order bit of the leftmost byte of a numeric field. + long result = 0; + for (int pos = length - 8; pos < length; pos++) + { + result = result << 8 | header[offset + pos]; + } + return result; + } + return ParseOctal(header, offset, length); + } + + /// + /// Parse an octal string from a header buffer. + /// + /// The header buffer from which to parse. + /// The offset into the buffer from which to parse. + /// The number of header bytes to parse. + /// The long equivalent of the octal string. + static public long ParseOctal(byte[] header, int offset, int length) + { + if (header == null) + { + throw new ArgumentNullException(nameof(header)); + } + + long result = 0; + bool stillPadding = true; + + int end = offset + length; + for (int i = offset; i < end; ++i) + { + if (header[i] == 0) + { + break; + } + + if (header[i] == (byte)' ' || header[i] == '0') + { + if (stillPadding) + { + continue; + } + + if (header[i] == (byte)' ') + { + break; + } + } + + stillPadding = false; + + result = (result << 3) + (header[i] - '0'); + } + + return result; + } + + /// + /// Parse a name from a header buffer. + /// + /// + /// The header buffer from which to parse. + /// + /// + /// The offset into the buffer from which to parse. + /// + /// + /// The number of header bytes to parse. + /// + /// + /// The name parsed. + /// + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + static public StringBuilder ParseName(byte[] header, int offset, int length) + { + return ParseName(header, offset, length, null); + } + + /// + /// Parse a name from a header buffer. + /// + /// + /// The header buffer from which to parse. + /// + /// + /// The offset into the buffer from which to parse. + /// + /// + /// The number of header bytes to parse. + /// + /// + /// name encoding, or null for ASCII only + /// + /// + /// The name parsed. + /// + static public StringBuilder ParseName(byte[] header, int offset, int length, Encoding encoding) + { + if (header == null) + { + throw new ArgumentNullException(nameof(header)); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be less than zero"); + } + + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length), "Cannot be less than zero"); + } + + if (offset + length > header.Length) + { + throw new ArgumentException("Exceeds header size", nameof(length)); + } + + var result = new StringBuilder(length); + + int count = 0; + if(encoding == null) + { + for (int i = offset; i < offset + length; ++i) + { + if (header[i] == 0) + { + break; + } + result.Append((char)header[i]); + } + } + else + { + for(int i = offset; i < offset + length; ++i, ++count) + { + if(header[i] == 0) + { + break; + } + } + result.Append(encoding.GetString(header, offset, count)); + } + + return result; + } + + /// + /// Add name to the buffer as a collection of bytes + /// + /// The name to add + /// The offset of the first character + /// The buffer to add to + /// The index of the first byte to add + /// The number of characters/bytes to add + /// The next free index in the + public static int GetNameBytes(StringBuilder name, int nameOffset, byte[] buffer, int bufferOffset, int length) + { + return GetNameBytes(name.ToString(), nameOffset, buffer, bufferOffset, length, null); + } + + /// + /// Add name to the buffer as a collection of bytes + /// + /// The name to add + /// The offset of the first character + /// The buffer to add to + /// The index of the first byte to add + /// The number of characters/bytes to add + /// The next free index in the + public static int GetNameBytes(string name, int nameOffset, byte[] buffer, int bufferOffset, int length) + { + return GetNameBytes(name, nameOffset, buffer, bufferOffset, length, null); + } + + /// + /// Add name to the buffer as a collection of bytes + /// + /// The name to add + /// The offset of the first character + /// The buffer to add to + /// The index of the first byte to add + /// The number of characters/bytes to add + /// name encoding, or null for ASCII only + /// The next free index in the + public static int GetNameBytes(string name, int nameOffset, byte[] buffer, int bufferOffset, int length, Encoding encoding) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + int i; + if(encoding != null) + { + // it can be more sufficient if using Span or unsafe + var nameArray = name.ToCharArray(nameOffset, Math.Min(name.Length - nameOffset, length)); + // it can be more sufficient if using Span(or unsafe?) and ArrayPool for temporary buffer + var bytes = encoding.GetBytes(nameArray, 0, nameArray.Length); + i = Math.Min(bytes.Length, length); + Array.Copy(bytes, 0, buffer, bufferOffset, i); + } + else + { + for (i = 0; i < length && nameOffset + i < name.Length; ++i) + { + buffer[bufferOffset + i] = (byte)name[nameOffset + i]; + } + } + + for (; i < length; ++i) + { + buffer[bufferOffset + i] = 0; + } + return bufferOffset + length; + } + /// + /// Add an entry name to the buffer + /// + /// + /// The name to add + /// + /// + /// The buffer to add to + /// + /// + /// The offset into the buffer from which to start adding + /// + /// + /// The number of header bytes to add + /// + /// + /// The index of the next free byte in the buffer + /// + /// TODO: what should be default behavior?(omit upper byte or UTF8?) + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public static int GetNameBytes(StringBuilder name, byte[] buffer, int offset, int length) + { + return GetNameBytes(name, buffer, offset, length, null); + } + + /// + /// Add an entry name to the buffer + /// + /// + /// The name to add + /// + /// + /// The buffer to add to + /// + /// + /// The offset into the buffer from which to start adding + /// + /// + /// The number of header bytes to add + /// + /// + /// + /// + /// The index of the next free byte in the buffer + /// + public static int GetNameBytes(StringBuilder name, byte[] buffer, int offset, int length, Encoding encoding) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + return GetNameBytes(name.ToString(), 0, buffer, offset, length, encoding); + } + + /// + /// Add an entry name to the buffer + /// + /// The name to add + /// The buffer to add to + /// The offset into the buffer from which to start adding + /// The number of header bytes to add + /// The index of the next free byte in the buffer + /// TODO: what should be default behavior?(omit upper byte or UTF8?) + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public static int GetNameBytes(string name, byte[] buffer, int offset, int length) + { + return GetNameBytes(name, buffer, offset, length, null); + } + + /// + /// Add an entry name to the buffer + /// + /// The name to add + /// The buffer to add to + /// The offset into the buffer from which to start adding + /// The number of header bytes to add + /// + /// The index of the next free byte in the buffer + public static int GetNameBytes(string name, byte[] buffer, int offset, int length, Encoding encoding) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + return GetNameBytes(name, 0, buffer, offset, length, encoding); + } + /// + /// Add a string to a buffer as a collection of ascii bytes. + /// + /// The string to add + /// The offset of the first character to add. + /// The buffer to add to. + /// The offset to start adding at. + /// The number of ascii characters to add. + /// The next free index in the buffer. + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public static int GetAsciiBytes(string toAdd, int nameOffset, byte[] buffer, int bufferOffset, int length) + { + return GetAsciiBytes(toAdd, nameOffset, buffer, bufferOffset, length, null); + } + + /// + /// Add a string to a buffer as a collection of ascii bytes. + /// + /// The string to add + /// The offset of the first character to add. + /// The buffer to add to. + /// The offset to start adding at. + /// The number of ascii characters to add. + /// String encoding, or null for ASCII only + /// The next free index in the buffer. + public static int GetAsciiBytes(string toAdd, int nameOffset, byte[] buffer, int bufferOffset, int length, Encoding encoding) + { + if (toAdd == null) + { + throw new ArgumentNullException(nameof(toAdd)); + } + + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + int i; + if(encoding == null) + { + for (i = 0; i < length && nameOffset + i < toAdd.Length; ++i) + { + buffer[bufferOffset + i] = (byte)toAdd[nameOffset + i]; + } + } + else + { + // It can be more sufficient if using unsafe code or Span(ToCharArray can be omitted) + var chars = toAdd.ToCharArray(); + // It can be more sufficient if using Span(or unsafe?) and ArrayPool for temporary buffer + var bytes = encoding.GetBytes(chars, nameOffset, Math.Min(toAdd.Length - nameOffset, length)); + i = Math.Min(bytes.Length, length); + Array.Copy(bytes, 0, buffer, bufferOffset, i); + } + // If length is beyond the toAdd string length (which is OK by the prev loop condition), eg if a field has fixed length and the string is shorter, make sure all of the extra chars are written as NULLs, so that the reader func would ignore them and get back the original string + for (; i < length; ++i) + buffer[bufferOffset + i] = 0; + return bufferOffset + length; + } + + /// + /// Put an octal representation of a value into a buffer + /// + /// + /// the value to be converted to octal + /// + /// + /// buffer to store the octal string + /// + /// + /// The offset into the buffer where the value starts + /// + /// + /// The length of the octal string to create + /// + /// + /// The offset of the character next byte after the octal string + /// + public static int GetOctalBytes(long value, byte[] buffer, int offset, int length) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + int localIndex = length - 1; + + // Either a space or null is valid here. We use NULL as per GNUTar + buffer[offset + localIndex] = 0; + --localIndex; + + if (value > 0) + { + for (long v = value; (localIndex >= 0) && (v > 0); --localIndex) + { + buffer[offset + localIndex] = (byte)((byte)'0' + (byte)(v & 7)); + v >>= 3; + } + } + + for (; localIndex >= 0; --localIndex) + { + buffer[offset + localIndex] = (byte)'0'; + } + + return offset + length; + } + + /// + /// Put an octal or binary representation of a value into a buffer + /// + /// Value to be convert to octal + /// The buffer to update + /// The offset into the buffer to store the value + /// The length of the octal string. Must be 12. + /// Index of next byte + private static int GetBinaryOrOctalBytes(long value, byte[] buffer, int offset, int length) + { + if (value > 0x1FFFFFFFF) + { // Octal 77777777777 (11 digits) + // Put value as binary, right-justified into the buffer. Set high order bit of left-most byte. + for (int pos = length - 1; pos > 0; pos--) + { + buffer[offset + pos] = (byte)value; + value = value >> 8; + } + buffer[offset] = 0x80; + return offset + length; + } + return GetOctalBytes(value, buffer, offset, length); + } + + /// + /// Add the checksum integer to header buffer. + /// + /// + /// The header buffer to set the checksum for + /// The offset into the buffer for the checksum + /// The number of header bytes to update. + /// It's formatted differently from the other fields: it has 6 digits, a + /// null, then a space -- rather than digits, a space, then a null. + /// The final space is already there, from checksumming + /// + /// The modified buffer offset + private static void GetCheckSumOctalBytes(long value, byte[] buffer, int offset, int length) + { + GetOctalBytes(value, buffer, offset, length - 1); + } + + /// + /// Compute the checksum for a tar entry header. + /// The checksum field must be all spaces prior to this happening + /// + /// The tar entry's header buffer. + /// The computed checksum. + private static int ComputeCheckSum(byte[] buffer) + { + int sum = 0; + for (int i = 0; i < buffer.Length; ++i) + { + sum += buffer[i]; + } + return sum; + } + + /// + /// Make a checksum for a tar entry ignoring the checksum contents. + /// + /// The tar entry's header buffer. + /// The checksum for the buffer + private static int MakeCheckSum(byte[] buffer) + { + int sum = 0; + for (int i = 0; i < CHKSUMOFS; ++i) + { + sum += buffer[i]; + } + + for (int i = 0; i < CHKSUMLEN; ++i) + { + sum += (byte)' '; + } + + for (int i = CHKSUMOFS + CHKSUMLEN; i < buffer.Length; ++i) + { + sum += buffer[i]; + } + return sum; + } + + private static int GetCTime(DateTime dateTime) + { + return unchecked((int)((dateTime.Ticks - dateTime1970.Ticks) / timeConversionFactor)); + } + + private static DateTime GetDateTimeFromCTime(long ticks) + { + DateTime result; + + try + { + result = new DateTime(dateTime1970.Ticks + ticks * timeConversionFactor); + } + catch (ArgumentOutOfRangeException) + { + result = dateTime1970; + } + return result; + } + + #region Instance Fields + + private string name; + private int mode; + private int userId; + private int groupId; + private long size; + private DateTime modTime; + private int checksum; + private bool isChecksumValid; + private byte typeFlag; + private string linkName; + private string magic; + private string version; + private string userName; + private string groupName; + private int devMajor; + private int devMinor; + + #endregion Instance Fields + + #region Class Fields + + // Values used during recursive operations. + static internal int userIdAsSet; + + static internal int groupIdAsSet; + static internal string userNameAsSet; + static internal string groupNameAsSet = "None"; + + static internal int defaultUserId; + static internal int defaultGroupId; + static internal string defaultGroupName = "None"; + static internal string defaultUser; + + #endregion Class Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs b/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs new file mode 100644 index 000000000000..f1a3622debcb --- /dev/null +++ b/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs @@ -0,0 +1,771 @@ +using System; +using System.IO; +using System.Text; + +namespace ICSharpCode.SharpZipLib.Tar +{ + /// + /// The TarInputStream reads a UNIX tar archive as an InputStream. + /// methods are provided to position at each successive entry in + /// the archive, and the read each entry as a normal input stream + /// using read(). + /// + public class TarInputStream : Stream + { + #region Constructors + + /// + /// Construct a TarInputStream with default block factor + /// + /// stream to source data from + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public TarInputStream(Stream inputStream) + : this(inputStream, TarBuffer.DefaultBlockFactor, null) + { + } + /// + /// Construct a TarInputStream with default block factor + /// + /// stream to source data from + /// The used for the Name fields, or null for ASCII only + public TarInputStream(Stream inputStream, Encoding nameEncoding) + : this(inputStream, TarBuffer.DefaultBlockFactor, nameEncoding) + { + } + + /// + /// Construct a TarInputStream with user specified block factor + /// + /// stream to source data from + /// block factor to apply to archive + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public TarInputStream(Stream inputStream, int blockFactor) + { + this.inputStream = inputStream; + tarBuffer = TarBuffer.CreateInputTarBuffer(inputStream, blockFactor); + encoding = null; + } + + /// + /// Construct a TarInputStream with user specified block factor + /// + /// stream to source data from + /// block factor to apply to archive + /// The used for the Name fields, or null for ASCII only + public TarInputStream(Stream inputStream, int blockFactor, Encoding nameEncoding) + { + this.inputStream = inputStream; + tarBuffer = TarBuffer.CreateInputTarBuffer(inputStream, blockFactor); + encoding = nameEncoding; + } + + #endregion Constructors + + /// + /// Gets or sets a flag indicating ownership of underlying stream. + /// When the flag is true will close the underlying stream also. + /// + /// The default value is true. + public bool IsStreamOwner + { + get { return tarBuffer.IsStreamOwner; } + set { tarBuffer.IsStreamOwner = value; } + } + + #region Stream Overrides + + /// + /// Gets a value indicating whether the current stream supports reading + /// + public override bool CanRead + { + get + { + return inputStream.CanRead; + } + } + + /// + /// Gets a value indicating whether the current stream supports seeking + /// This property always returns false. + /// + public override bool CanSeek + { + get + { + return false; + } + } + + /// + /// Gets a value indicating if the stream supports writing. + /// This property always returns false. + /// + public override bool CanWrite + { + get + { + return false; + } + } + + /// + /// The length in bytes of the stream + /// + public override long Length + { + get + { + return inputStream.Length; + } + } + + /// + /// Gets or sets the position within the stream. + /// Setting the Position is not supported and throws a NotSupportedExceptionNotSupportedException + /// + /// Any attempt to set position + public override long Position + { + get + { + return inputStream.Position; + } + set + { + throw new NotSupportedException("TarInputStream Seek not supported"); + } + } + + /// + /// Flushes the baseInputStream + /// + public override void Flush() + { + inputStream.Flush(); + } + + /// + /// Set the streams position. This operation is not supported and will throw a NotSupportedException + /// + /// The offset relative to the origin to seek to. + /// The to start seeking from. + /// The new position in the stream. + /// Any access + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("TarInputStream Seek not supported"); + } + + /// + /// Sets the length of the stream + /// This operation is not supported and will throw a NotSupportedException + /// + /// The new stream length. + /// Any access + public override void SetLength(long value) + { + throw new NotSupportedException("TarInputStream SetLength not supported"); + } + + /// + /// Writes a block of bytes to this stream using data from a buffer. + /// This operation is not supported and will throw a NotSupportedException + /// + /// The buffer containing bytes to write. + /// The offset in the buffer of the frist byte to write. + /// The number of bytes to write. + /// Any access + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException("TarInputStream Write not supported"); + } + + /// + /// Writes a byte to the current position in the file stream. + /// This operation is not supported and will throw a NotSupportedException + /// + /// The byte value to write. + /// Any access + public override void WriteByte(byte value) + { + throw new NotSupportedException("TarInputStream WriteByte not supported"); + } + + /// + /// Reads a byte from the current tar archive entry. + /// + /// A byte cast to an int; -1 if the at the end of the stream. + public override int ReadByte() + { + byte[] oneByteBuffer = new byte[1]; + int num = Read(oneByteBuffer, 0, 1); + if (num <= 0) + { + // return -1 to indicate that no byte was read. + return -1; + } + return oneByteBuffer[0]; + } + + /// + /// Reads bytes from the current tar archive entry. + /// + /// This method is aware of the boundaries of the current + /// entry in the archive and will deal with them appropriately + /// + /// + /// The buffer into which to place bytes read. + /// + /// + /// The offset at which to place bytes read. + /// + /// + /// The number of bytes to read. + /// + /// + /// The number of bytes read, or 0 at end of stream/EOF. + /// + public override int Read(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + int totalRead = 0; + + if (entryOffset >= entrySize) + { + return 0; + } + + long numToRead = count; + + if ((numToRead + entryOffset) > entrySize) + { + numToRead = entrySize - entryOffset; + } + + if (readBuffer != null) + { + int sz = (numToRead > readBuffer.Length) ? readBuffer.Length : (int)numToRead; + + Array.Copy(readBuffer, 0, buffer, offset, sz); + + if (sz >= readBuffer.Length) + { + readBuffer = null; + } + else + { + int newLen = readBuffer.Length - sz; + byte[] newBuf = new byte[newLen]; + Array.Copy(readBuffer, sz, newBuf, 0, newLen); + readBuffer = newBuf; + } + + totalRead += sz; + numToRead -= sz; + offset += sz; + } + + while (numToRead > 0) + { + byte[] rec = tarBuffer.ReadBlock(); + if (rec == null) + { + // Unexpected EOF! + throw new TarException("unexpected EOF with " + numToRead + " bytes unread"); + } + + var sz = (int)numToRead; + int recLen = rec.Length; + + if (recLen > sz) + { + Array.Copy(rec, 0, buffer, offset, sz); + readBuffer = new byte[recLen - sz]; + Array.Copy(rec, sz, readBuffer, 0, recLen - sz); + } + else + { + sz = recLen; + Array.Copy(rec, 0, buffer, offset, recLen); + } + + totalRead += sz; + numToRead -= sz; + offset += sz; + } + + entryOffset += totalRead; + + return totalRead; + } + + /// + /// Closes this stream. Calls the TarBuffer's close() method. + /// The underlying stream is closed by the TarBuffer. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + tarBuffer.Close(); + } + } + + #endregion Stream Overrides + + /// + /// Set the entry factory for this instance. + /// + /// The factory for creating new entries + public void SetEntryFactory(IEntryFactory factory) + { + entryFactory = factory; + } + + /// + /// Get the record size being used by this stream's TarBuffer. + /// + public int RecordSize + { + get { return tarBuffer.RecordSize; } + } + + /// + /// Get the record size being used by this stream's TarBuffer. + /// + /// + /// TarBuffer record size. + /// + [Obsolete("Use RecordSize property instead")] + public int GetRecordSize() + { + return tarBuffer.RecordSize; + } + + /// + /// Get the available data that can be read from the current + /// entry in the archive. This does not indicate how much data + /// is left in the entire archive, only in the current entry. + /// This value is determined from the entry's size header field + /// and the amount of data already read from the current entry. + /// + /// + /// The number of available bytes for the current entry. + /// + public long Available + { + get + { + return entrySize - entryOffset; + } + } + + /// + /// Skip bytes in the input buffer. This skips bytes in the + /// current entry's data, not the entire archive, and will + /// stop at the end of the current entry's data if the number + /// to skip extends beyond that point. + /// + /// + /// The number of bytes to skip. + /// + public void Skip(long skipCount) + { + // TODO: REVIEW efficiency of TarInputStream.Skip + // This is horribly inefficient, but it ensures that we + // properly skip over bytes via the TarBuffer... + // + byte[] skipBuf = new byte[8 * 1024]; + + for (long num = skipCount; num > 0;) + { + int toRead = num > skipBuf.Length ? skipBuf.Length : (int)num; + int numRead = Read(skipBuf, 0, toRead); + + if (numRead == -1) + { + break; + } + + num -= numRead; + } + } + + /// + /// Return a value of true if marking is supported; false otherwise. + /// + /// Currently marking is not supported, the return value is always false. + public bool IsMarkSupported + { + get + { + return false; + } + } + + /// + /// Since we do not support marking just yet, we do nothing. + /// + /// + /// The limit to mark. + /// + public void Mark(int markLimit) + { + } + + /// + /// Since we do not support marking just yet, we do nothing. + /// + public void Reset() + { + } + + /// + /// Get the next entry in this tar archive. This will skip + /// over any remaining data in the current entry, if there + /// is one, and place the input stream at the header of the + /// next entry, and read the header and instantiate a new + /// TarEntry from the header bytes and return that entry. + /// If there are no more entries in the archive, null will + /// be returned to indicate that the end of the archive has + /// been reached. + /// + /// + /// The next TarEntry in the archive, or null. + /// + public TarEntry GetNextEntry() + { + if (hasHitEOF) + { + return null; + } + + if (currentEntry != null) + { + SkipToNextEntry(); + } + + byte[] headerBuf = tarBuffer.ReadBlock(); + + if (headerBuf == null) + { + hasHitEOF = true; + } + else if (TarBuffer.IsEndOfArchiveBlock(headerBuf)) + { + hasHitEOF = true; + + // Read the second zero-filled block + tarBuffer.ReadBlock(); + } + else + { + hasHitEOF = false; + } + + if (hasHitEOF) + { + currentEntry = null; + } + else + { + try + { + var header = new TarHeader(); + header.ParseBuffer(headerBuf, encoding); + if (!header.IsChecksumValid) + { + throw new TarException("Header checksum is invalid"); + } + this.entryOffset = 0; + this.entrySize = header.Size; + + StringBuilder longName = null; + + if (header.TypeFlag == TarHeader.LF_GNU_LONGNAME) + { + byte[] nameBuffer = new byte[TarBuffer.BlockSize]; + long numToRead = this.entrySize; + + longName = new StringBuilder(); + + while (numToRead > 0) + { + int numRead = this.Read(nameBuffer, 0, (numToRead > nameBuffer.Length ? nameBuffer.Length : (int)numToRead)); + + if (numRead == -1) + { + throw new InvalidHeaderException("Failed to read long name entry"); + } + + longName.Append(TarHeader.ParseName(nameBuffer, 0, numRead, encoding).ToString()); + numToRead -= numRead; + } + + SkipToNextEntry(); + headerBuf = this.tarBuffer.ReadBlock(); + } + else if (header.TypeFlag == TarHeader.LF_GHDR) + { // POSIX global extended header + // Ignore things we dont understand completely for now + SkipToNextEntry(); + headerBuf = this.tarBuffer.ReadBlock(); + } + else if (header.TypeFlag == TarHeader.LF_XHDR) + { // POSIX extended header + byte[] nameBuffer = new byte[TarBuffer.BlockSize]; + long numToRead = this.entrySize; + + var xhr = new TarExtendedHeaderReader(); + + while (numToRead > 0) + { + int numRead = this.Read(nameBuffer, 0, (numToRead > nameBuffer.Length ? nameBuffer.Length : (int)numToRead)); + + if (numRead == -1) + { + throw new InvalidHeaderException("Failed to read long name entry"); + } + + xhr.Read(nameBuffer, numRead); + numToRead -= numRead; + } + + if (xhr.Headers.TryGetValue("path", out string name)) + { + longName = new StringBuilder(name); + } + + SkipToNextEntry(); + headerBuf = this.tarBuffer.ReadBlock(); + } + else if (header.TypeFlag == TarHeader.LF_GNU_VOLHDR) + { + // TODO: could show volume name when verbose + SkipToNextEntry(); + headerBuf = this.tarBuffer.ReadBlock(); + } + else if (header.TypeFlag != TarHeader.LF_NORMAL && + header.TypeFlag != TarHeader.LF_OLDNORM && + header.TypeFlag != TarHeader.LF_LINK && + header.TypeFlag != TarHeader.LF_SYMLINK && + header.TypeFlag != TarHeader.LF_DIR) + { + // Ignore things we dont understand completely for now + SkipToNextEntry(); + headerBuf = tarBuffer.ReadBlock(); + } + + if (entryFactory == null) + { + currentEntry = new TarEntry(headerBuf, encoding); + if (longName != null) + { + currentEntry.Name = longName.ToString(); + } + } + else + { + currentEntry = entryFactory.CreateEntry(headerBuf); + } + + // Magic was checked here for 'ustar' but there are multiple valid possibilities + // so this is not done anymore. + + entryOffset = 0; + + // TODO: Review How do we resolve this discrepancy?! + entrySize = this.currentEntry.Size; + } + catch (InvalidHeaderException ex) + { + entrySize = 0; + entryOffset = 0; + currentEntry = null; + string errorText = string.Format("Bad header in record {0} block {1} {2}", + tarBuffer.CurrentRecord, tarBuffer.CurrentBlock, ex.Message); + throw new InvalidHeaderException(errorText); + } + } + return currentEntry; + } + + /// + /// Copies the contents of the current tar archive entry directly into + /// an output stream. + /// + /// + /// The OutputStream into which to write the entry's data. + /// + public void CopyEntryContents(Stream outputStream) + { + byte[] tempBuffer = new byte[32 * 1024]; + + while (true) + { + int numRead = Read(tempBuffer, 0, tempBuffer.Length); + if (numRead <= 0) + { + break; + } + outputStream.Write(tempBuffer, 0, numRead); + } + } + + private void SkipToNextEntry() + { + long numToSkip = entrySize - entryOffset; + + if (numToSkip > 0) + { + Skip(numToSkip); + } + + readBuffer = null; + } + + /// + /// This interface is provided, along with the method , to allow + /// the programmer to have their own subclass instantiated for the + /// entries return from . + /// + public interface IEntryFactory + { + // This interface does not considering name encoding. + // How this interface should be? + /// + /// Create an entry based on name alone + /// + /// + /// Name of the new EntryPointNotFoundException to create + /// + /// created TarEntry or descendant class + TarEntry CreateEntry(string name); + + /// + /// Create an instance based on an actual file + /// + /// + /// Name of file to represent in the entry + /// + /// + /// Created TarEntry or descendant class + /// + TarEntry CreateEntryFromFile(string fileName); + + /// + /// Create a tar entry based on the header information passed + /// + /// + /// Buffer containing header information to create an entry from. + /// + /// + /// Created TarEntry or descendant class + /// + TarEntry CreateEntry(byte[] headerBuffer); + } + + /// + /// Standard entry factory class creating instances of the class TarEntry + /// + public class EntryFactoryAdapter : IEntryFactory + { + Encoding nameEncoding; + /// + /// Construct standard entry factory class with ASCII name encoding + /// + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public EntryFactoryAdapter() + { + } + /// + /// Construct standard entry factory with name encoding + /// + /// The used for the Name fields, or null for ASCII only + public EntryFactoryAdapter(Encoding nameEncoding) + { + this.nameEncoding = nameEncoding; + } + /// + /// Create a based on named + /// + /// The name to use for the entry + /// A new + public TarEntry CreateEntry(string name) + { + return TarEntry.CreateTarEntry(name); + } + + /// + /// Create a tar entry with details obtained from file + /// + /// The name of the file to retrieve details from. + /// A new + public TarEntry CreateEntryFromFile(string fileName) + { + return TarEntry.CreateEntryFromFile(fileName); + } + + /// + /// Create an entry based on details in header + /// + /// The buffer containing entry details. + /// A new + public TarEntry CreateEntry(byte[] headerBuffer) + { + return new TarEntry(headerBuffer, nameEncoding); + } + } + + #region Instance Fields + + /// + /// Flag set when last block has been read + /// + protected bool hasHitEOF; + + /// + /// Size of this entry as recorded in header + /// + protected long entrySize; + + /// + /// Number of bytes read for this entry so far + /// + protected long entryOffset; + + /// + /// Buffer used with calls to Read() + /// + protected byte[] readBuffer; + + /// + /// Working buffer + /// + protected TarBuffer tarBuffer; + + /// + /// Current entry being read + /// + private TarEntry currentEntry; + + /// + /// Factory used to create TarEntry or descendant class instance + /// + protected IEntryFactory entryFactory; + + /// + /// Stream used as the source of input data. + /// + private readonly Stream inputStream; + + private readonly Encoding encoding; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Tar/TarOutputStream.cs b/ICSharpCode.SharpZipLib/Tar/TarOutputStream.cs new file mode 100644 index 000000000000..7c52e6c7c092 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Tar/TarOutputStream.cs @@ -0,0 +1,522 @@ +using System; +using System.IO; +using System.Text; + +namespace ICSharpCode.SharpZipLib.Tar +{ + /// + /// The TarOutputStream writes a UNIX tar archive as an OutputStream. + /// Methods are provided to put entries, and then write their contents + /// by writing to this stream using write(). + /// + /// public + public class TarOutputStream : Stream + { + #region Constructors + + /// + /// Construct TarOutputStream using default block factor + /// + /// stream to write to + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public TarOutputStream(Stream outputStream) + : this(outputStream, TarBuffer.DefaultBlockFactor) + { + } + + /// + /// Construct TarOutputStream using default block factor + /// + /// stream to write to + /// The used for the Name fields, or null for ASCII only + public TarOutputStream(Stream outputStream, Encoding nameEncoding) + : this(outputStream, TarBuffer.DefaultBlockFactor, nameEncoding) + { + } + + /// + /// Construct TarOutputStream with user specified block factor + /// + /// stream to write to + /// blocking factor + [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] + public TarOutputStream(Stream outputStream, int blockFactor) + { + if (outputStream == null) + { + throw new ArgumentNullException(nameof(outputStream)); + } + + this.outputStream = outputStream; + buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor); + + assemblyBuffer = new byte[TarBuffer.BlockSize]; + blockBuffer = new byte[TarBuffer.BlockSize]; + } + + /// + /// Construct TarOutputStream with user specified block factor + /// + /// stream to write to + /// blocking factor + /// The used for the Name fields, or null for ASCII only + public TarOutputStream(Stream outputStream, int blockFactor, Encoding nameEncoding) + { + if (outputStream == null) + { + throw new ArgumentNullException(nameof(outputStream)); + } + + this.outputStream = outputStream; + buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor); + + assemblyBuffer = new byte[TarBuffer.BlockSize]; + blockBuffer = new byte[TarBuffer.BlockSize]; + + this.nameEncoding = nameEncoding; + } + + #endregion Constructors + + /// + /// Gets or sets a flag indicating ownership of underlying stream. + /// When the flag is true will close the underlying stream also. + /// + /// The default value is true. + public bool IsStreamOwner + { + get { return buffer.IsStreamOwner; } + set { buffer.IsStreamOwner = value; } + } + + /// + /// true if the stream supports reading; otherwise, false. + /// + public override bool CanRead + { + get + { + return outputStream.CanRead; + } + } + + /// + /// true if the stream supports seeking; otherwise, false. + /// + public override bool CanSeek + { + get + { + return outputStream.CanSeek; + } + } + + /// + /// true if stream supports writing; otherwise, false. + /// + public override bool CanWrite + { + get + { + return outputStream.CanWrite; + } + } + + /// + /// length of stream in bytes + /// + public override long Length + { + get + { + return outputStream.Length; + } + } + + /// + /// gets or sets the position within the current stream. + /// + public override long Position + { + get + { + return outputStream.Position; + } + set + { + outputStream.Position = value; + } + } + + /// + /// set the position within the current stream + /// + /// The offset relative to the to seek to + /// The to seek from. + /// The new position in the stream. + public override long Seek(long offset, SeekOrigin origin) + { + return outputStream.Seek(offset, origin); + } + + /// + /// Set the length of the current stream + /// + /// The new stream length. + public override void SetLength(long value) + { + outputStream.SetLength(value); + } + + /// + /// Read a byte from the stream and advance the position within the stream + /// by one byte or returns -1 if at the end of the stream. + /// + /// The byte value or -1 if at end of stream + public override int ReadByte() + { + return outputStream.ReadByte(); + } + + /// + /// read bytes from the current stream and advance the position within the + /// stream by the number of bytes read. + /// + /// The buffer to store read bytes in. + /// The index into the buffer to being storing bytes at. + /// The desired number of bytes to read. + /// The total number of bytes read, or zero if at the end of the stream. + /// The number of bytes may be less than the count + /// requested if data is not available. + public override int Read(byte[] buffer, int offset, int count) + { + return outputStream.Read(buffer, offset, count); + } + + /// + /// All buffered data is written to destination + /// + public override void Flush() + { + outputStream.Flush(); + } + + /// + /// Ends the TAR archive without closing the underlying OutputStream. + /// The result is that the EOF block of nulls is written. + /// + public void Finish() + { + if (IsEntryOpen) + { + CloseEntry(); + } + WriteEofBlock(); + } + + /// + /// Ends the TAR archive and closes the underlying OutputStream. + /// + /// This means that Finish() is called followed by calling the + /// TarBuffer's Close(). + protected override void Dispose(bool disposing) + { + if (!isClosed) + { + isClosed = true; + Finish(); + buffer.Close(); + } + } + + /// + /// Get the record size being used by this stream's TarBuffer. + /// + public int RecordSize + { + get { return buffer.RecordSize; } + } + + /// + /// Get the record size being used by this stream's TarBuffer. + /// + /// + /// The TarBuffer record size. + /// + [Obsolete("Use RecordSize property instead")] + public int GetRecordSize() + { + return buffer.RecordSize; + } + + /// + /// Get a value indicating whether an entry is open, requiring more data to be written. + /// + private bool IsEntryOpen + { + get { return (currBytes < currSize); } + } + + /// + /// Put an entry on the output stream. This writes the entry's + /// header and positions the output stream for writing + /// the contents of the entry. Once this method is called, the + /// stream is ready for calls to write() to write the entry's + /// contents. Once the contents are written, closeEntry() + /// MUST be called to ensure that all buffered data + /// is completely written to the output stream. + /// + /// + /// The TarEntry to be written to the archive. + /// + public void PutNextEntry(TarEntry entry) + { + if (entry == null) + { + throw new ArgumentNullException(nameof(entry)); + } + + var namelen = nameEncoding != null ? nameEncoding.GetByteCount(entry.TarHeader.Name) : entry.TarHeader.Name.Length; + + if (namelen > TarHeader.NAMELEN) + { + var longHeader = new TarHeader(); + longHeader.TypeFlag = TarHeader.LF_GNU_LONGNAME; + longHeader.Name = longHeader.Name + "././@LongLink"; + longHeader.Mode = 420;//644 by default + longHeader.UserId = entry.UserId; + longHeader.GroupId = entry.GroupId; + longHeader.GroupName = entry.GroupName; + longHeader.UserName = entry.UserName; + longHeader.LinkName = ""; + longHeader.Size = namelen + 1; // Plus one to avoid dropping last char + + longHeader.WriteHeader(blockBuffer, nameEncoding); + buffer.WriteBlock(blockBuffer); // Add special long filename header block + + int nameCharIndex = 0; + + while (nameCharIndex < namelen + 1 /* we've allocated one for the null char, now we must make sure it gets written out */) + { + Array.Clear(blockBuffer, 0, blockBuffer.Length); + TarHeader.GetAsciiBytes(entry.TarHeader.Name, nameCharIndex, this.blockBuffer, 0, TarBuffer.BlockSize, nameEncoding); // This func handles OK the extra char out of string length + nameCharIndex += TarBuffer.BlockSize; + buffer.WriteBlock(blockBuffer); + } + } + + entry.WriteEntryHeader(blockBuffer, nameEncoding); + buffer.WriteBlock(blockBuffer); + + currBytes = 0; + + currSize = entry.IsDirectory ? 0 : entry.Size; + } + + /// + /// Close an entry. This method MUST be called for all file + /// entries that contain data. The reason is that we must + /// buffer data written to the stream in order to satisfy + /// the buffer's block based writes. Thus, there may be + /// data fragments still being assembled that must be written + /// to the output stream before this entry is closed and the + /// next entry written. + /// + public void CloseEntry() + { + if (assemblyBufferLength > 0) + { + Array.Clear(assemblyBuffer, assemblyBufferLength, assemblyBuffer.Length - assemblyBufferLength); + + buffer.WriteBlock(assemblyBuffer); + + currBytes += assemblyBufferLength; + assemblyBufferLength = 0; + } + + if (currBytes < currSize) + { + string errorText = string.Format( + "Entry closed at '{0}' before the '{1}' bytes specified in the header were written", + currBytes, currSize); + throw new TarException(errorText); + } + } + + /// + /// Writes a byte to the current tar archive entry. + /// This method simply calls Write(byte[], int, int). + /// + /// + /// The byte to be written. + /// + public override void WriteByte(byte value) + { + Write(new byte[] { value }, 0, 1); + } + + /// + /// Writes bytes to the current tar archive entry. This method + /// is aware of the current entry and will throw an exception if + /// you attempt to write bytes past the length specified for the + /// current entry. The method is also (painfully) aware of the + /// record buffering required by TarBuffer, and manages buffers + /// that are not a multiple of recordsize in length, including + /// assembling records from small buffers. + /// + /// + /// The buffer to write to the archive. + /// + /// + /// The offset in the buffer from which to get bytes. + /// + /// + /// The number of bytes to write. + /// + public override void Write(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative"); + } + + if (buffer.Length - offset < count) + { + throw new ArgumentException("offset and count combination is invalid"); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative"); + } + + if ((currBytes + count) > currSize) + { + string errorText = string.Format("request to write '{0}' bytes exceeds size in header of '{1}' bytes", + count, this.currSize); + throw new ArgumentOutOfRangeException(nameof(count), errorText); + } + + // + // We have to deal with assembly!!! + // The programmer can be writing little 32 byte chunks for all + // we know, and we must assemble complete blocks for writing. + // TODO REVIEW Maybe this should be in TarBuffer? Could that help to + // eliminate some of the buffer copying. + // + if (assemblyBufferLength > 0) + { + if ((assemblyBufferLength + count) >= blockBuffer.Length) + { + int aLen = blockBuffer.Length - assemblyBufferLength; + + Array.Copy(assemblyBuffer, 0, blockBuffer, 0, assemblyBufferLength); + Array.Copy(buffer, offset, blockBuffer, assemblyBufferLength, aLen); + + this.buffer.WriteBlock(blockBuffer); + + currBytes += blockBuffer.Length; + + offset += aLen; + count -= aLen; + + assemblyBufferLength = 0; + } + else + { + Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count); + offset += count; + assemblyBufferLength += count; + count -= count; + } + } + + // + // When we get here we have EITHER: + // o An empty "assembly" buffer. + // o No bytes to write (count == 0) + // + while (count > 0) + { + if (count < blockBuffer.Length) + { + Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count); + assemblyBufferLength += count; + break; + } + + this.buffer.WriteBlock(buffer, offset); + + int bufferLength = blockBuffer.Length; + currBytes += bufferLength; + count -= bufferLength; + offset += bufferLength; + } + } + + /// + /// Write an EOF (end of archive) block to the tar archive. + /// The end of the archive is indicated by two blocks consisting entirely of zero bytes. + /// + private void WriteEofBlock() + { + Array.Clear(blockBuffer, 0, blockBuffer.Length); + buffer.WriteBlock(blockBuffer); + buffer.WriteBlock(blockBuffer); + } + + #region Instance Fields + + /// + /// bytes written for this entry so far + /// + private long currBytes; + + /// + /// current 'Assembly' buffer length + /// + private int assemblyBufferLength; + + /// + /// Flag indicating whether this instance has been closed or not. + /// + private bool isClosed; + + /// + /// Size for the current entry + /// + protected long currSize; + + /// + /// single block working buffer + /// + protected byte[] blockBuffer; + + /// + /// 'Assembly' buffer used to assemble data before writing + /// + protected byte[] assemblyBuffer; + + /// + /// TarBuffer used to provide correct blocking factor + /// + protected TarBuffer buffer; + + /// + /// the destination stream for the archive contents + /// + protected Stream outputStream; + + /// + /// name encoding + /// + protected Encoding nameEncoding; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Deflater.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Deflater.cs new file mode 100644 index 000000000000..3dbe98c8d9bd --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/Deflater.cs @@ -0,0 +1,604 @@ +using System; + +namespace ICSharpCode.SharpZipLib.Zip.Compression +{ + /// + /// This is the Deflater class. The deflater class compresses input + /// with the deflate algorithm described in RFC 1951. It has several + /// compression levels and three different strategies described below. + /// + /// This class is not thread safe. This is inherent in the API, due + /// to the split of deflate and setInput. + /// + /// author of the original java version : Jochen Hoenicke + /// + public class Deflater + { + #region Deflater Documentation + + /* + * The Deflater can do the following state transitions: + * + * (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---. + * / | (2) (5) | + * / v (5) | + * (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3) + * \ | (3) | ,--------' + * | | | (3) / + * v v (5) v v + * (1) -> BUSY_STATE ----> FINISHING_STATE + * | (6) + * v + * FINISHED_STATE + * \_____________________________________/ + * | (7) + * v + * CLOSED_STATE + * + * (1) If we should produce a header we start in INIT_STATE, otherwise + * we start in BUSY_STATE. + * (2) A dictionary may be set only when we are in INIT_STATE, then + * we change the state as indicated. + * (3) Whether a dictionary is set or not, on the first call of deflate + * we change to BUSY_STATE. + * (4) -- intentionally left blank -- :) + * (5) FINISHING_STATE is entered, when flush() is called to indicate that + * there is no more INPUT. There are also states indicating, that + * the header wasn't written yet. + * (6) FINISHED_STATE is entered, when everything has been flushed to the + * internal pending output buffer. + * (7) At any time (7) + * + */ + + #endregion Deflater Documentation + + #region Public Constants + + /// + /// The best and slowest compression level. This tries to find very + /// long and distant string repetitions. + /// + public const int BEST_COMPRESSION = 9; + + /// + /// The worst but fastest compression level. + /// + public const int BEST_SPEED = 1; + + /// + /// The default compression level. + /// + public const int DEFAULT_COMPRESSION = -1; + + /// + /// This level won't compress at all but output uncompressed blocks. + /// + public const int NO_COMPRESSION = 0; + + /// + /// The compression method. This is the only method supported so far. + /// There is no need to use this constant at all. + /// + public const int DEFLATED = 8; + + #endregion Public Constants + + #region Public Enum + + /// + /// Compression Level as an enum for safer use + /// + public enum CompressionLevel + { + /// + /// The best and slowest compression level. This tries to find very + /// long and distant string repetitions. + /// + BEST_COMPRESSION = Deflater.BEST_COMPRESSION, + + /// + /// The worst but fastest compression level. + /// + BEST_SPEED = Deflater.BEST_SPEED, + + /// + /// The default compression level. + /// + DEFAULT_COMPRESSION = Deflater.DEFAULT_COMPRESSION, + + /// + /// This level won't compress at all but output uncompressed blocks. + /// + NO_COMPRESSION = Deflater.NO_COMPRESSION, + + /// + /// The compression method. This is the only method supported so far. + /// There is no need to use this constant at all. + /// + DEFLATED = Deflater.DEFLATED + } + + #endregion Public Enum + + #region Local Constants + + private const int IS_SETDICT = 0x01; + private const int IS_FLUSHING = 0x04; + private const int IS_FINISHING = 0x08; + + private const int INIT_STATE = 0x00; + private const int SETDICT_STATE = 0x01; + + // private static int INIT_FINISHING_STATE = 0x08; + // private static int SETDICT_FINISHING_STATE = 0x09; + private const int BUSY_STATE = 0x10; + + private const int FLUSHING_STATE = 0x14; + private const int FINISHING_STATE = 0x1c; + private const int FINISHED_STATE = 0x1e; + private const int CLOSED_STATE = 0x7f; + + #endregion Local Constants + + #region Constructors + + /// + /// Creates a new deflater with default compression level. + /// + public Deflater() : this(DEFAULT_COMPRESSION, false) + { + } + + /// + /// Creates a new deflater with given compression level. + /// + /// + /// the compression level, a value between NO_COMPRESSION + /// and BEST_COMPRESSION, or DEFAULT_COMPRESSION. + /// + /// if lvl is out of range. + public Deflater(int level) : this(level, false) + { + } + + /// + /// Creates a new deflater with given compression level. + /// + /// + /// the compression level, a value between NO_COMPRESSION + /// and BEST_COMPRESSION. + /// + /// + /// true, if we should suppress the Zlib/RFC1950 header at the + /// beginning and the adler checksum at the end of the output. This is + /// useful for the GZIP/PKZIP formats. + /// + /// if lvl is out of range. + public Deflater(int level, bool noZlibHeaderOrFooter) + { + if (level == DEFAULT_COMPRESSION) + { + level = 6; + } + else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) + { + throw new ArgumentOutOfRangeException(nameof(level)); + } + + pending = new DeflaterPending(); + engine = new DeflaterEngine(pending, noZlibHeaderOrFooter); + this.noZlibHeaderOrFooter = noZlibHeaderOrFooter; + SetStrategy(DeflateStrategy.Default); + SetLevel(level); + Reset(); + } + + #endregion Constructors + + /// + /// Resets the deflater. The deflater acts afterwards as if it was + /// just created with the same compression level and strategy as it + /// had before. + /// + public void Reset() + { + state = (noZlibHeaderOrFooter ? BUSY_STATE : INIT_STATE); + totalOut = 0; + pending.Reset(); + engine.Reset(); + } + + /// + /// Gets the current adler checksum of the data that was processed so far. + /// + public int Adler + { + get + { + return engine.Adler; + } + } + + /// + /// Gets the number of input bytes processed so far. + /// + public long TotalIn + { + get + { + return engine.TotalIn; + } + } + + /// + /// Gets the number of output bytes so far. + /// + public long TotalOut + { + get + { + return totalOut; + } + } + + /// + /// Flushes the current input block. Further calls to deflate() will + /// produce enough output to inflate everything in the current input + /// block. This is not part of Sun's JDK so I have made it package + /// private. It is used by DeflaterOutputStream to implement + /// flush(). + /// + public void Flush() + { + state |= IS_FLUSHING; + } + + /// + /// Finishes the deflater with the current input block. It is an error + /// to give more input after this method was called. This method must + /// be called to force all bytes to be flushed. + /// + public void Finish() + { + state |= (IS_FLUSHING | IS_FINISHING); + } + + /// + /// Returns true if the stream was finished and no more output bytes + /// are available. + /// + public bool IsFinished + { + get + { + return (state == FINISHED_STATE) && pending.IsFlushed; + } + } + + /// + /// Returns true, if the input buffer is empty. + /// You should then call setInput(). + /// NOTE: This method can also return true when the stream + /// was finished. + /// + public bool IsNeedingInput + { + get + { + return engine.NeedsInput(); + } + } + + /// + /// Sets the data which should be compressed next. This should be only + /// called when needsInput indicates that more input is needed. + /// If you call setInput when needsInput() returns false, the + /// previous input that is still pending will be thrown away. + /// The given byte array should not be changed, before needsInput() returns + /// true again. + /// This call is equivalent to setInput(input, 0, input.length). + /// + /// + /// the buffer containing the input data. + /// + /// + /// if the buffer was finished() or ended(). + /// + public void SetInput(byte[] input) + { + SetInput(input, 0, input.Length); + } + + /// + /// Sets the data which should be compressed next. This should be + /// only called when needsInput indicates that more input is needed. + /// The given byte array should not be changed, before needsInput() returns + /// true again. + /// + /// + /// the buffer containing the input data. + /// + /// + /// the start of the data. + /// + /// + /// the number of data bytes of input. + /// + /// + /// if the buffer was Finish()ed or if previous input is still pending. + /// + public void SetInput(byte[] input, int offset, int count) + { + if ((state & IS_FINISHING) != 0) + { + throw new InvalidOperationException("Finish() already called"); + } + engine.SetInput(input, offset, count); + } + + /// + /// Sets the compression level. There is no guarantee of the exact + /// position of the change, but if you call this when needsInput is + /// true the change of compression level will occur somewhere near + /// before the end of the so far given input. + /// + /// + /// the new compression level. + /// + public void SetLevel(int level) + { + if (level == DEFAULT_COMPRESSION) + { + level = 6; + } + else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) + { + throw new ArgumentOutOfRangeException(nameof(level)); + } + + if (this.level != level) + { + this.level = level; + engine.SetLevel(level); + } + } + + /// + /// Get current compression level + /// + /// Returns the current compression level + public int GetLevel() + { + return level; + } + + /// + /// Sets the compression strategy. Strategy is one of + /// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact + /// position where the strategy is changed, the same as for + /// SetLevel() applies. + /// + /// + /// The new compression strategy. + /// + public void SetStrategy(DeflateStrategy strategy) + { + engine.Strategy = strategy; + } + + /// + /// Deflates the current input block with to the given array. + /// + /// + /// The buffer where compressed data is stored + /// + /// + /// The number of compressed bytes added to the output, or 0 if either + /// IsNeedingInput() or IsFinished returns true or length is zero. + /// + public int Deflate(byte[] output) + { + return Deflate(output, 0, output.Length); + } + + /// + /// Deflates the current input block to the given array. + /// + /// + /// Buffer to store the compressed data. + /// + /// + /// Offset into the output array. + /// + /// + /// The maximum number of bytes that may be stored. + /// + /// + /// The number of compressed bytes added to the output, or 0 if either + /// needsInput() or finished() returns true or length is zero. + /// + /// + /// If Finish() was previously called. + /// + /// + /// If offset or length don't match the array length. + /// + public int Deflate(byte[] output, int offset, int length) + { + int origLength = length; + + if (state == CLOSED_STATE) + { + throw new InvalidOperationException("Deflater closed"); + } + + if (state < BUSY_STATE) + { + // output header + int header = (DEFLATED + + ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8; + int level_flags = (level - 1) >> 1; + if (level_flags < 0 || level_flags > 3) + { + level_flags = 3; + } + header |= level_flags << 6; + if ((state & IS_SETDICT) != 0) + { + // Dictionary was set + header |= DeflaterConstants.PRESET_DICT; + } + header += 31 - (header % 31); + + pending.WriteShortMSB(header); + if ((state & IS_SETDICT) != 0) + { + int chksum = engine.Adler; + engine.ResetAdler(); + pending.WriteShortMSB(chksum >> 16); + pending.WriteShortMSB(chksum & 0xffff); + } + + state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING)); + } + + for (; ; ) + { + int count = pending.Flush(output, offset, length); + offset += count; + totalOut += count; + length -= count; + + if (length == 0 || state == FINISHED_STATE) + { + break; + } + + if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) + { + switch (state) + { + case BUSY_STATE: + // We need more input now + return origLength - length; + + case FLUSHING_STATE: + if (level != NO_COMPRESSION) + { + /* We have to supply some lookahead. 8 bit lookahead + * is needed by the zlib inflater, and we must fill + * the next byte, so that all bits are flushed. + */ + int neededbits = 8 + ((-pending.BitCount) & 7); + while (neededbits > 0) + { + /* write a static tree block consisting solely of + * an EOF: + */ + pending.WriteBits(2, 10); + neededbits -= 10; + } + } + state = BUSY_STATE; + break; + + case FINISHING_STATE: + pending.AlignToByte(); + + // Compressed data is complete. Write footer information if required. + if (!noZlibHeaderOrFooter) + { + int adler = engine.Adler; + pending.WriteShortMSB(adler >> 16); + pending.WriteShortMSB(adler & 0xffff); + } + state = FINISHED_STATE; + break; + } + } + } + return origLength - length; + } + + /// + /// Sets the dictionary which should be used in the deflate process. + /// This call is equivalent to setDictionary(dict, 0, dict.Length). + /// + /// + /// the dictionary. + /// + /// + /// if SetInput () or Deflate () were already called or another dictionary was already set. + /// + public void SetDictionary(byte[] dictionary) + { + SetDictionary(dictionary, 0, dictionary.Length); + } + + /// + /// Sets the dictionary which should be used in the deflate process. + /// The dictionary is a byte array containing strings that are + /// likely to occur in the data which should be compressed. The + /// dictionary is not stored in the compressed output, only a + /// checksum. To decompress the output you need to supply the same + /// dictionary again. + /// + /// + /// The dictionary data + /// + /// + /// The index where dictionary information commences. + /// + /// + /// The number of bytes in the dictionary. + /// + /// + /// If SetInput () or Deflate() were already called or another dictionary was already set. + /// + public void SetDictionary(byte[] dictionary, int index, int count) + { + if (state != INIT_STATE) + { + throw new InvalidOperationException(); + } + + state = SETDICT_STATE; + engine.SetDictionary(dictionary, index, count); + } + + #region Instance Fields + + /// + /// Compression level. + /// + private int level; + + /// + /// If true no Zlib/RFC1950 headers or footers are generated + /// + private bool noZlibHeaderOrFooter; + + /// + /// The current state. + /// + private int state; + + /// + /// The total bytes of output written. + /// + private long totalOut; + + /// + /// The pending output. + /// + private DeflaterPending pending; + + /// + /// The deflater engine. + /// + private DeflaterEngine engine; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterConstants.cs b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterConstants.cs new file mode 100644 index 000000000000..b7c7d2a6994b --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterConstants.cs @@ -0,0 +1,146 @@ +using System; + +namespace ICSharpCode.SharpZipLib.Zip.Compression +{ + /// + /// This class contains constants used for deflation. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "kept for backwards compatibility")] + public static class DeflaterConstants + { + /// + /// Set to true to enable debugging + /// + public const bool DEBUGGING = false; + + /// + /// Written to Zip file to identify a stored block + /// + public const int STORED_BLOCK = 0; + + /// + /// Identifies static tree in Zip file + /// + public const int STATIC_TREES = 1; + + /// + /// Identifies dynamic tree in Zip file + /// + public const int DYN_TREES = 2; + + /// + /// Header flag indicating a preset dictionary for deflation + /// + public const int PRESET_DICT = 0x20; + + /// + /// Sets internal buffer sizes for Huffman encoding + /// + public const int DEFAULT_MEM_LEVEL = 8; + + /// + /// Internal compression engine constant + /// + public const int MAX_MATCH = 258; + + /// + /// Internal compression engine constant + /// + public const int MIN_MATCH = 3; + + /// + /// Internal compression engine constant + /// + public const int MAX_WBITS = 15; + + /// + /// Internal compression engine constant + /// + public const int WSIZE = 1 << MAX_WBITS; + + /// + /// Internal compression engine constant + /// + public const int WMASK = WSIZE - 1; + + /// + /// Internal compression engine constant + /// + public const int HASH_BITS = DEFAULT_MEM_LEVEL + 7; + + /// + /// Internal compression engine constant + /// + public const int HASH_SIZE = 1 << HASH_BITS; + + /// + /// Internal compression engine constant + /// + public const int HASH_MASK = HASH_SIZE - 1; + + /// + /// Internal compression engine constant + /// + public const int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH; + + /// + /// Internal compression engine constant + /// + public const int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; + + /// + /// Internal compression engine constant + /// + public const int MAX_DIST = WSIZE - MIN_LOOKAHEAD; + + /// + /// Internal compression engine constant + /// + public const int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8); + + /// + /// Internal compression engine constant + /// + public static int MAX_BLOCK_SIZE = Math.Min(65535, PENDING_BUF_SIZE - 5); + + /// + /// Internal compression engine constant + /// + public const int DEFLATE_STORED = 0; + + /// + /// Internal compression engine constant + /// + public const int DEFLATE_FAST = 1; + + /// + /// Internal compression engine constant + /// + public const int DEFLATE_SLOW = 2; + + /// + /// Internal compression engine constant + /// + public static int[] GOOD_LENGTH = { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 }; + + /// + /// Internal compression engine constant + /// + public static int[] MAX_LAZY = { 0, 4, 5, 6, 4, 16, 16, 32, 128, 258 }; + + /// + /// Internal compression engine constant + /// + public static int[] NICE_LENGTH = { 0, 8, 16, 32, 16, 32, 128, 128, 258, 258 }; + + /// + /// Internal compression engine constant + /// + public static int[] MAX_CHAIN = { 0, 4, 8, 32, 16, 32, 128, 256, 1024, 4096 }; + + /// + /// Internal compression engine constant + /// + public static int[] COMPR_FUNC = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs new file mode 100644 index 000000000000..556911c4033c --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs @@ -0,0 +1,946 @@ +using ICSharpCode.SharpZipLib.Checksum; +using System; + +namespace ICSharpCode.SharpZipLib.Zip.Compression +{ + /// + /// Strategies for deflater + /// + public enum DeflateStrategy + { + /// + /// The default strategy + /// + Default = 0, + + /// + /// This strategy will only allow longer string repetitions. It is + /// useful for random data with a small character set. + /// + Filtered = 1, + + /// + /// This strategy will not look for string repetitions at all. It + /// only encodes with Huffman trees (which means, that more common + /// characters get a smaller encoding. + /// + HuffmanOnly = 2 + } + + // DEFLATE ALGORITHM: + // + // The uncompressed stream is inserted into the window array. When + // the window array is full the first half is thrown away and the + // second half is copied to the beginning. + // + // The head array is a hash table. Three characters build a hash value + // and they the value points to the corresponding index in window of + // the last string with this hash. The prev array implements a + // linked list of matches with the same hash: prev[index & WMASK] points + // to the previous index with the same hash. + // + + /// + /// Low level compression engine for deflate algorithm which uses a 32K sliding window + /// with secondary compression from Huffman/Shannon-Fano codes. + /// + public class DeflaterEngine + { + #region Constants + + private const int TooFar = 4096; + + #endregion Constants + + #region Constructors + + /// + /// Construct instance with pending buffer + /// Adler calculation will be performed + /// + /// + /// Pending buffer to use + /// + public DeflaterEngine(DeflaterPending pending) + : this (pending, false) + { + } + + + + /// + /// Construct instance with pending buffer + /// + /// + /// Pending buffer to use + /// + /// + /// If no adler calculation should be performed + /// + public DeflaterEngine(DeflaterPending pending, bool noAdlerCalculation) + { + this.pending = pending; + huffman = new DeflaterHuffman(pending); + if (!noAdlerCalculation) + adler = new Adler32(); + + window = new byte[2 * DeflaterConstants.WSIZE]; + head = new short[DeflaterConstants.HASH_SIZE]; + prev = new short[DeflaterConstants.WSIZE]; + + // We start at index 1, to avoid an implementation deficiency, that + // we cannot build a repeat pattern at index 0. + blockStart = strstart = 1; + } + + #endregion Constructors + + /// + /// Deflate drives actual compression of data + /// + /// True to flush input buffers + /// Finish deflation with the current input. + /// Returns true if progress has been made. + public bool Deflate(bool flush, bool finish) + { + bool progress; + do + { + FillWindow(); + bool canFlush = flush && (inputOff == inputEnd); + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) { + Console.WriteLine("window: [" + blockStart + "," + strstart + "," + + lookahead + "], " + compressionFunction + "," + canFlush); + } +#endif + switch (compressionFunction) + { + case DeflaterConstants.DEFLATE_STORED: + progress = DeflateStored(canFlush, finish); + break; + + case DeflaterConstants.DEFLATE_FAST: + progress = DeflateFast(canFlush, finish); + break; + + case DeflaterConstants.DEFLATE_SLOW: + progress = DeflateSlow(canFlush, finish); + break; + + default: + throw new InvalidOperationException("unknown compressionFunction"); + } + } while (pending.IsFlushed && progress); // repeat while we have no pending output and progress was made + return progress; + } + + /// + /// Sets input data to be deflated. Should only be called when NeedsInput() + /// returns true + /// + /// The buffer containing input data. + /// The offset of the first byte of data. + /// The number of bytes of data to use as input. + public void SetInput(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (inputOff < inputEnd) + { + throw new InvalidOperationException("Old input was not completely processed"); + } + + int end = offset + count; + + /* We want to throw an ArrayIndexOutOfBoundsException early. The + * check is very tricky: it also handles integer wrap around. + */ + if ((offset > end) || (end > buffer.Length)) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + inputBuf = buffer; + inputOff = offset; + inputEnd = end; + } + + /// + /// Determines if more input is needed. + /// + /// Return true if input is needed via SetInput + public bool NeedsInput() + { + return (inputEnd == inputOff); + } + + /// + /// Set compression dictionary + /// + /// The buffer containing the dictionary data + /// The offset in the buffer for the first byte of data + /// The length of the dictionary data. + public void SetDictionary(byte[] buffer, int offset, int length) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (strstart != 1) ) + { + throw new InvalidOperationException("strstart not 1"); + } +#endif + adler?.Update(new ArraySegment(buffer, offset, length)); + if (length < DeflaterConstants.MIN_MATCH) + { + return; + } + + if (length > DeflaterConstants.MAX_DIST) + { + offset += length - DeflaterConstants.MAX_DIST; + length = DeflaterConstants.MAX_DIST; + } + + System.Array.Copy(buffer, offset, window, strstart, length); + + UpdateHash(); + --length; + while (--length > 0) + { + InsertString(); + strstart++; + } + strstart += 2; + blockStart = strstart; + } + + /// + /// Reset internal state + /// + public void Reset() + { + huffman.Reset(); + adler?.Reset(); + blockStart = strstart = 1; + lookahead = 0; + totalIn = 0; + prevAvailable = false; + matchLen = DeflaterConstants.MIN_MATCH - 1; + + for (int i = 0; i < DeflaterConstants.HASH_SIZE; i++) + { + head[i] = 0; + } + + for (int i = 0; i < DeflaterConstants.WSIZE; i++) + { + prev[i] = 0; + } + } + + /// + /// Reset Adler checksum + /// + public void ResetAdler() + { + adler?.Reset(); + } + + /// + /// Get current value of Adler checksum + /// + public int Adler + { + get + { + return (adler != null) ? unchecked((int)adler.Value) : 0; + } + } + + /// + /// Total data processed + /// + public long TotalIn + { + get + { + return totalIn; + } + } + + /// + /// Get/set the deflate strategy + /// + public DeflateStrategy Strategy + { + get + { + return strategy; + } + set + { + strategy = value; + } + } + + /// + /// Set the deflate level (0-9) + /// + /// The value to set the level to. + public void SetLevel(int level) + { + if ((level < 0) || (level > 9)) + { + throw new ArgumentOutOfRangeException(nameof(level)); + } + + goodLength = DeflaterConstants.GOOD_LENGTH[level]; + max_lazy = DeflaterConstants.MAX_LAZY[level]; + niceLength = DeflaterConstants.NICE_LENGTH[level]; + max_chain = DeflaterConstants.MAX_CHAIN[level]; + + if (DeflaterConstants.COMPR_FUNC[level] != compressionFunction) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) { + Console.WriteLine("Change from " + compressionFunction + " to " + + DeflaterConstants.COMPR_FUNC[level]); + } +#endif + switch (compressionFunction) + { + case DeflaterConstants.DEFLATE_STORED: + if (strstart > blockStart) + { + huffman.FlushStoredBlock(window, blockStart, + strstart - blockStart, false); + blockStart = strstart; + } + UpdateHash(); + break; + + case DeflaterConstants.DEFLATE_FAST: + if (strstart > blockStart) + { + huffman.FlushBlock(window, blockStart, strstart - blockStart, + false); + blockStart = strstart; + } + break; + + case DeflaterConstants.DEFLATE_SLOW: + if (prevAvailable) + { + huffman.TallyLit(window[strstart - 1] & 0xff); + } + if (strstart > blockStart) + { + huffman.FlushBlock(window, blockStart, strstart - blockStart, false); + blockStart = strstart; + } + prevAvailable = false; + matchLen = DeflaterConstants.MIN_MATCH - 1; + break; + } + compressionFunction = DeflaterConstants.COMPR_FUNC[level]; + } + } + + /// + /// Fill the window + /// + public void FillWindow() + { + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (strstart >= DeflaterConstants.WSIZE + DeflaterConstants.MAX_DIST) + { + SlideWindow(); + } + + /* If there is not enough lookahead, but still some input left, + * read in the input + */ + if (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd) + { + int more = 2 * DeflaterConstants.WSIZE - lookahead - strstart; + + if (more > inputEnd - inputOff) + { + more = inputEnd - inputOff; + } + + System.Array.Copy(inputBuf, inputOff, window, strstart + lookahead, more); + adler?.Update(new ArraySegment(inputBuf, inputOff, more)); + + inputOff += more; + totalIn += more; + lookahead += more; + } + + if (lookahead >= DeflaterConstants.MIN_MATCH) + { + UpdateHash(); + } + } + + private void UpdateHash() + { + /* + if (DEBUGGING) { + Console.WriteLine("updateHash: "+strstart); + } + */ + ins_h = (window[strstart] << DeflaterConstants.HASH_SHIFT) ^ window[strstart + 1]; + } + + /// + /// Inserts the current string in the head hash and returns the previous + /// value for this hash. + /// + /// The previous hash value + private int InsertString() + { + short match; + int hash = ((ins_h << DeflaterConstants.HASH_SHIFT) ^ window[strstart + (DeflaterConstants.MIN_MATCH - 1)]) & DeflaterConstants.HASH_MASK; + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) + { + if (hash != (((window[strstart] << (2*HASH_SHIFT)) ^ + (window[strstart + 1] << HASH_SHIFT) ^ + (window[strstart + 2])) & HASH_MASK)) { + throw new SharpZipBaseException("hash inconsistent: " + hash + "/" + +window[strstart] + "," + +window[strstart + 1] + "," + +window[strstart + 2] + "," + HASH_SHIFT); + } + } +#endif + prev[strstart & DeflaterConstants.WMASK] = match = head[hash]; + head[hash] = unchecked((short)strstart); + ins_h = hash; + return match & 0xffff; + } + + private void SlideWindow() + { + Array.Copy(window, DeflaterConstants.WSIZE, window, 0, DeflaterConstants.WSIZE); + matchStart -= DeflaterConstants.WSIZE; + strstart -= DeflaterConstants.WSIZE; + blockStart -= DeflaterConstants.WSIZE; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). + for (int i = 0; i < DeflaterConstants.HASH_SIZE; ++i) + { + int m = head[i] & 0xffff; + head[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0); + } + + // Slide the prev table. + for (int i = 0; i < DeflaterConstants.WSIZE; i++) + { + int m = prev[i] & 0xffff; + prev[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0); + } + } + + /// + /// Find the best (longest) string in the window matching the + /// string starting at strstart. + /// + /// Preconditions: + /// + /// strstart + DeflaterConstants.MAX_MATCH <= window.length. + /// + /// + /// True if a match greater than the minimum length is found + private bool FindLongestMatch(int curMatch) + { + int match; + int scan = strstart; + // scanMax is the highest position that we can look at + int scanMax = scan + Math.Min(DeflaterConstants.MAX_MATCH, lookahead) - 1; + int limit = Math.Max(scan - DeflaterConstants.MAX_DIST, 0); + + byte[] window = this.window; + short[] prev = this.prev; + int chainLength = this.max_chain; + int niceLength = Math.Min(this.niceLength, lookahead); + + matchLen = Math.Max(matchLen, DeflaterConstants.MIN_MATCH - 1); + + if (scan + matchLen > scanMax) return false; + + byte scan_end1 = window[scan + matchLen - 1]; + byte scan_end = window[scan + matchLen]; + + // Do not waste too much time if we already have a good match: + if (matchLen >= this.goodLength) chainLength >>= 2; + + do + { + match = curMatch; + scan = strstart; + + if (window[match + matchLen] != scan_end + || window[match + matchLen - 1] != scan_end1 + || window[match] != window[scan] + || window[++match] != window[++scan]) + { + continue; + } + + // scan is set to strstart+1 and the comparison passed, so + // scanMax - scan is the maximum number of bytes we can compare. + // below we compare 8 bytes at a time, so first we compare + // (scanMax - scan) % 8 bytes, so the remainder is a multiple of 8 + + switch ((scanMax - scan) % 8) + { + case 1: + if (window[++scan] == window[++match]) break; + break; + + case 2: + if (window[++scan] == window[++match] + && window[++scan] == window[++match]) break; + break; + + case 3: + if (window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match]) break; + break; + + case 4: + if (window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match]) break; + break; + + case 5: + if (window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match]) break; + break; + + case 6: + if (window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match]) break; + break; + + case 7: + if (window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match]) break; + break; + } + + if (window[scan] == window[match]) + { + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart + 258 unless lookahead is + * exhausted first. + */ + do + { + if (scan == scanMax) + { + ++scan; // advance to first position not matched + ++match; + + break; + } + } + while (window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match]); + } + + if (scan - strstart > matchLen) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (ins_h == 0) ) + Console.Error.WriteLine("Found match: " + curMatch + "-" + (scan - strstart)); +#endif + + matchStart = curMatch; + matchLen = scan - strstart; + + if (matchLen >= niceLength) + break; + + scan_end1 = window[scan - 1]; + scan_end = window[scan]; + } + } while ((curMatch = (prev[curMatch & DeflaterConstants.WMASK] & 0xffff)) > limit && 0 != --chainLength); + + return matchLen >= DeflaterConstants.MIN_MATCH; + } + + private bool DeflateStored(bool flush, bool finish) + { + if (!flush && (lookahead == 0)) + { + return false; + } + + strstart += lookahead; + lookahead = 0; + + int storedLength = strstart - blockStart; + + if ((storedLength >= DeflaterConstants.MAX_BLOCK_SIZE) || // Block is full + (blockStart < DeflaterConstants.WSIZE && storedLength >= DeflaterConstants.MAX_DIST) || // Block may move out of window + flush) + { + bool lastBlock = finish; + if (storedLength > DeflaterConstants.MAX_BLOCK_SIZE) + { + storedLength = DeflaterConstants.MAX_BLOCK_SIZE; + lastBlock = false; + } + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) + { + Console.WriteLine("storedBlock[" + storedLength + "," + lastBlock + "]"); + } +#endif + + huffman.FlushStoredBlock(window, blockStart, storedLength, lastBlock); + blockStart += storedLength; + return !(lastBlock || storedLength == 0); + } + return true; + } + + private bool DeflateFast(bool flush, bool finish) + { + if (lookahead < DeflaterConstants.MIN_LOOKAHEAD && !flush) + { + return false; + } + + while (lookahead >= DeflaterConstants.MIN_LOOKAHEAD || flush) + { + if (lookahead == 0) + { + // We are flushing everything + huffman.FlushBlock(window, blockStart, strstart - blockStart, finish); + blockStart = strstart; + return false; + } + + if (strstart > 2 * DeflaterConstants.WSIZE - DeflaterConstants.MIN_LOOKAHEAD) + { + /* slide window, as FindLongestMatch needs this. + * This should only happen when flushing and the window + * is almost full. + */ + SlideWindow(); + } + + int hashHead; + if (lookahead >= DeflaterConstants.MIN_MATCH && + (hashHead = InsertString()) != 0 && + strategy != DeflateStrategy.HuffmanOnly && + strstart - hashHead <= DeflaterConstants.MAX_DIST && + FindLongestMatch(hashHead)) + { + // longestMatch sets matchStart and matchLen +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) + { + for (int i = 0 ; i < matchLen; i++) { + if (window[strstart + i] != window[matchStart + i]) { + throw new SharpZipBaseException("Match failure"); + } + } + } +#endif + + bool full = huffman.TallyDist(strstart - matchStart, matchLen); + + lookahead -= matchLen; + if (matchLen <= max_lazy && lookahead >= DeflaterConstants.MIN_MATCH) + { + while (--matchLen > 0) + { + ++strstart; + InsertString(); + } + ++strstart; + } + else + { + strstart += matchLen; + if (lookahead >= DeflaterConstants.MIN_MATCH - 1) + { + UpdateHash(); + } + } + matchLen = DeflaterConstants.MIN_MATCH - 1; + if (!full) + { + continue; + } + } + else + { + // No match found + huffman.TallyLit(window[strstart] & 0xff); + ++strstart; + --lookahead; + } + + if (huffman.IsFull()) + { + bool lastBlock = finish && (lookahead == 0); + huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock); + blockStart = strstart; + return !lastBlock; + } + } + return true; + } + + private bool DeflateSlow(bool flush, bool finish) + { + if (lookahead < DeflaterConstants.MIN_LOOKAHEAD && !flush) + { + return false; + } + + while (lookahead >= DeflaterConstants.MIN_LOOKAHEAD || flush) + { + if (lookahead == 0) + { + if (prevAvailable) + { + huffman.TallyLit(window[strstart - 1] & 0xff); + } + prevAvailable = false; + + // We are flushing everything +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && !flush) + { + throw new SharpZipBaseException("Not flushing, but no lookahead"); + } +#endif + huffman.FlushBlock(window, blockStart, strstart - blockStart, + finish); + blockStart = strstart; + return false; + } + + if (strstart >= 2 * DeflaterConstants.WSIZE - DeflaterConstants.MIN_LOOKAHEAD) + { + /* slide window, as FindLongestMatch needs this. + * This should only happen when flushing and the window + * is almost full. + */ + SlideWindow(); + } + + int prevMatch = matchStart; + int prevLen = matchLen; + if (lookahead >= DeflaterConstants.MIN_MATCH) + { + int hashHead = InsertString(); + + if (strategy != DeflateStrategy.HuffmanOnly && + hashHead != 0 && + strstart - hashHead <= DeflaterConstants.MAX_DIST && + FindLongestMatch(hashHead)) + { + // longestMatch sets matchStart and matchLen + + // Discard match if too small and too far away + if (matchLen <= 5 && (strategy == DeflateStrategy.Filtered || (matchLen == DeflaterConstants.MIN_MATCH && strstart - matchStart > TooFar))) + { + matchLen = DeflaterConstants.MIN_MATCH - 1; + } + } + } + + // previous match was better + if ((prevLen >= DeflaterConstants.MIN_MATCH) && (matchLen <= prevLen)) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) + { + for (int i = 0 ; i < matchLen; i++) { + if (window[strstart-1+i] != window[prevMatch + i]) + throw new SharpZipBaseException(); + } + } +#endif + huffman.TallyDist(strstart - 1 - prevMatch, prevLen); + prevLen -= 2; + do + { + strstart++; + lookahead--; + if (lookahead >= DeflaterConstants.MIN_MATCH) + { + InsertString(); + } + } while (--prevLen > 0); + + strstart++; + lookahead--; + prevAvailable = false; + matchLen = DeflaterConstants.MIN_MATCH - 1; + } + else + { + if (prevAvailable) + { + huffman.TallyLit(window[strstart - 1] & 0xff); + } + prevAvailable = true; + strstart++; + lookahead--; + } + + if (huffman.IsFull()) + { + int len = strstart - blockStart; + if (prevAvailable) + { + len--; + } + bool lastBlock = (finish && (lookahead == 0) && !prevAvailable); + huffman.FlushBlock(window, blockStart, len, lastBlock); + blockStart += len; + return !lastBlock; + } + } + return true; + } + + #region Instance Fields + + // Hash index of string to be inserted + private int ins_h; + + /// + /// Hashtable, hashing three characters to an index for window, so + /// that window[index]..window[index+2] have this hash code. + /// Note that the array should really be unsigned short, so you need + /// to and the values with 0xffff. + /// + private short[] head; + + /// + /// prev[index & WMASK] points to the previous index that has the + /// same hash code as the string starting at index. This way + /// entries with the same hash code are in a linked list. + /// Note that the array should really be unsigned short, so you need + /// to and the values with 0xffff. + /// + private short[] prev; + + private int matchStart; + + // Length of best match + private int matchLen; + + // Set if previous match exists + private bool prevAvailable; + + private int blockStart; + + /// + /// Points to the current character in the window. + /// + private int strstart; + + /// + /// lookahead is the number of characters starting at strstart in + /// window that are valid. + /// So window[strstart] until window[strstart+lookahead-1] are valid + /// characters. + /// + private int lookahead; + + /// + /// This array contains the part of the uncompressed stream that + /// is of relevance. The current character is indexed by strstart. + /// + private byte[] window; + + private DeflateStrategy strategy; + private int max_chain, max_lazy, niceLength, goodLength; + + /// + /// The current compression function. + /// + private int compressionFunction; + + /// + /// The input data for compression. + /// + private byte[] inputBuf; + + /// + /// The total bytes of input read. + /// + private long totalIn; + + /// + /// The offset into inputBuf, where input data starts. + /// + private int inputOff; + + /// + /// The end offset of the input data. + /// + private int inputEnd; + + private DeflaterPending pending; + private DeflaterHuffman huffman; + + /// + /// The adler checksum + /// + private Adler32 adler; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterHuffman.cs b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterHuffman.cs new file mode 100644 index 000000000000..2f71366fca72 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterHuffman.cs @@ -0,0 +1,959 @@ +using System; + +namespace ICSharpCode.SharpZipLib.Zip.Compression +{ + /// + /// This is the DeflaterHuffman class. + /// + /// This class is not thread safe. This is inherent in the API, due + /// to the split of Deflate and SetInput. + /// + /// author of the original java version : Jochen Hoenicke + /// + public class DeflaterHuffman + { + private const int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6); + private const int LITERAL_NUM = 286; + + // Number of distance codes + private const int DIST_NUM = 30; + + // Number of codes used to transfer bit lengths + private const int BITLEN_NUM = 19; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REP_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REP_11_138 = 18; + + private const int EOF_SYMBOL = 256; + + // The lengths of the bit length codes are sent in order of decreasing + // probability, to avoid transmitting the lengths for unused bit length codes. + private static readonly int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + private static readonly byte[] bit4Reverse = { + 0, + 8, + 4, + 12, + 2, + 10, + 6, + 14, + 1, + 9, + 5, + 13, + 3, + 11, + 7, + 15 + }; + + private static short[] staticLCodes; + private static byte[] staticLLength; + private static short[] staticDCodes; + private static byte[] staticDLength; + + private class Tree + { + #region Instance Fields + + public short[] freqs; + + public byte[] length; + + public int minNumCodes; + + public int numCodes; + + private short[] codes; + private readonly int[] bl_counts; + private readonly int maxLength; + private DeflaterHuffman dh; + + #endregion Instance Fields + + #region Constructors + + public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength) + { + this.dh = dh; + this.minNumCodes = minCodes; + this.maxLength = maxLength; + freqs = new short[elems]; + bl_counts = new int[maxLength]; + } + + #endregion Constructors + + /// + /// Resets the internal state of the tree + /// + public void Reset() + { + for (int i = 0; i < freqs.Length; i++) + { + freqs[i] = 0; + } + codes = null; + length = null; + } + + public void WriteSymbol(int code) + { + // if (DeflaterConstants.DEBUGGING) { + // freqs[code]--; + // // Console.Write("writeSymbol("+freqs.length+","+code+"): "); + // } + dh.pending.WriteBits(codes[code] & 0xffff, length[code]); + } + + /// + /// Check that all frequencies are zero + /// + /// + /// At least one frequency is non-zero + /// + public void CheckEmpty() + { + bool empty = true; + for (int i = 0; i < freqs.Length; i++) + { + empty &= freqs[i] == 0; + } + + if (!empty) + { + throw new SharpZipBaseException("!Empty"); + } + } + + /// + /// Set static codes and length + /// + /// new codes + /// length for new codes + public void SetStaticCodes(short[] staticCodes, byte[] staticLengths) + { + codes = staticCodes; + length = staticLengths; + } + + /// + /// Build dynamic codes and lengths + /// + public void BuildCodes() + { + int numSymbols = freqs.Length; + int[] nextCode = new int[maxLength]; + int code = 0; + + codes = new short[freqs.Length]; + + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("buildCodes: "+freqs.Length); + // } + + for (int bits = 0; bits < maxLength; bits++) + { + nextCode[bits] = code; + code += bl_counts[bits] << (15 - bits); + + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("bits: " + ( bits + 1) + " count: " + bl_counts[bits] + // +" nextCode: "+code); + // } + } + +#if DebugDeflation + if ( DeflaterConstants.DEBUGGING && (code != 65536) ) + { + throw new SharpZipBaseException("Inconsistent bl_counts!"); + } +#endif + for (int i = 0; i < numCodes; i++) + { + int bits = length[i]; + if (bits > 0) + { + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("codes["+i+"] = rev(" + nextCode[bits-1]+"), + // +bits); + // } + + codes[i] = BitReverse(nextCode[bits - 1]); + nextCode[bits - 1] += 1 << (16 - bits); + } + } + } + + public void BuildTree() + { + int numSymbols = freqs.Length; + + /* heap is a priority queue, sorted by frequency, least frequent + * nodes first. The heap is a binary tree, with the property, that + * the parent node is smaller than both child nodes. This assures + * that the smallest node is the first parent. + * + * The binary tree is encoded in an array: 0 is root node and + * the nodes 2*n+1, 2*n+2 are the child nodes of node n. + */ + int[] heap = new int[numSymbols]; + int heapLen = 0; + int maxCode = 0; + for (int n = 0; n < numSymbols; n++) + { + int freq = freqs[n]; + if (freq != 0) + { + // Insert n into heap + int pos = heapLen++; + int ppos; + while (pos > 0 && freqs[heap[ppos = (pos - 1) / 2]] > freq) + { + heap[pos] = heap[ppos]; + pos = ppos; + } + heap[pos] = n; + + maxCode = n; + } + } + + /* We could encode a single literal with 0 bits but then we + * don't see the literals. Therefore we force at least two + * literals to avoid this case. We don't care about order in + * this case, both literals get a 1 bit code. + */ + while (heapLen < 2) + { + int node = maxCode < 2 ? ++maxCode : 0; + heap[heapLen++] = node; + } + + numCodes = Math.Max(maxCode + 1, minNumCodes); + + int numLeafs = heapLen; + int[] childs = new int[4 * heapLen - 2]; + int[] values = new int[2 * heapLen - 1]; + int numNodes = numLeafs; + for (int i = 0; i < heapLen; i++) + { + int node = heap[i]; + childs[2 * i] = node; + childs[2 * i + 1] = -1; + values[i] = freqs[node] << 8; + heap[i] = i; + } + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do + { + int first = heap[0]; + int last = heap[--heapLen]; + + // Propagate the hole to the leafs of the heap + int ppos = 0; + int path = 1; + + while (path < heapLen) + { + if (path + 1 < heapLen && values[heap[path]] > values[heap[path + 1]]) + { + path++; + } + + heap[ppos] = heap[path]; + ppos = path; + path = path * 2 + 1; + } + + /* Now propagate the last element down along path. Normally + * it shouldn't go too deep. + */ + int lastVal = values[last]; + while ((path = ppos) > 0 && values[heap[ppos = (path - 1) / 2]] > lastVal) + { + heap[path] = heap[ppos]; + } + heap[path] = last; + + int second = heap[0]; + + // Create a new node father of first and second + last = numNodes++; + childs[2 * last] = first; + childs[2 * last + 1] = second; + int mindepth = Math.Min(values[first] & 0xff, values[second] & 0xff); + values[last] = lastVal = values[first] + values[second] - mindepth + 1; + + // Again, propagate the hole to the leafs + ppos = 0; + path = 1; + + while (path < heapLen) + { + if (path + 1 < heapLen && values[heap[path]] > values[heap[path + 1]]) + { + path++; + } + + heap[ppos] = heap[path]; + ppos = path; + path = ppos * 2 + 1; + } + + // Now propagate the new element down along path + while ((path = ppos) > 0 && values[heap[ppos = (path - 1) / 2]] > lastVal) + { + heap[path] = heap[ppos]; + } + heap[path] = last; + } while (heapLen > 1); + + if (heap[0] != childs.Length / 2 - 1) + { + throw new SharpZipBaseException("Heap invariant violated"); + } + + BuildLength(childs); + } + + /// + /// Get encoded length + /// + /// Encoded length, the sum of frequencies * lengths + public int GetEncodedLength() + { + int len = 0; + for (int i = 0; i < freqs.Length; i++) + { + len += freqs[i] * length[i]; + } + return len; + } + + /// + /// Scan a literal or distance tree to determine the frequencies of the codes + /// in the bit length tree. + /// + public void CalcBLFreq(Tree blTree) + { + int max_count; /* max repeat count */ + int min_count; /* min repeat count */ + int count; /* repeat count of the current code */ + int curlen = -1; /* length of current code */ + + int i = 0; + while (i < numCodes) + { + count = 1; + int nextlen = length[i]; + if (nextlen == 0) + { + max_count = 138; + min_count = 3; + } + else + { + max_count = 6; + min_count = 3; + if (curlen != nextlen) + { + blTree.freqs[nextlen]++; + count = 0; + } + } + curlen = nextlen; + i++; + + while (i < numCodes && curlen == length[i]) + { + i++; + if (++count >= max_count) + { + break; + } + } + + if (count < min_count) + { + blTree.freqs[curlen] += (short)count; + } + else if (curlen != 0) + { + blTree.freqs[REP_3_6]++; + } + else if (count <= 10) + { + blTree.freqs[REP_3_10]++; + } + else + { + blTree.freqs[REP_11_138]++; + } + } + } + + /// + /// Write tree values + /// + /// Tree to write + public void WriteTree(Tree blTree) + { + int max_count; // max repeat count + int min_count; // min repeat count + int count; // repeat count of the current code + int curlen = -1; // length of current code + + int i = 0; + while (i < numCodes) + { + count = 1; + int nextlen = length[i]; + if (nextlen == 0) + { + max_count = 138; + min_count = 3; + } + else + { + max_count = 6; + min_count = 3; + if (curlen != nextlen) + { + blTree.WriteSymbol(nextlen); + count = 0; + } + } + curlen = nextlen; + i++; + + while (i < numCodes && curlen == length[i]) + { + i++; + if (++count >= max_count) + { + break; + } + } + + if (count < min_count) + { + while (count-- > 0) + { + blTree.WriteSymbol(curlen); + } + } + else if (curlen != 0) + { + blTree.WriteSymbol(REP_3_6); + dh.pending.WriteBits(count - 3, 2); + } + else if (count <= 10) + { + blTree.WriteSymbol(REP_3_10); + dh.pending.WriteBits(count - 3, 3); + } + else + { + blTree.WriteSymbol(REP_11_138); + dh.pending.WriteBits(count - 11, 7); + } + } + } + + private void BuildLength(int[] childs) + { + this.length = new byte[freqs.Length]; + int numNodes = childs.Length / 2; + int numLeafs = (numNodes + 1) / 2; + int overflow = 0; + + for (int i = 0; i < maxLength; i++) + { + bl_counts[i] = 0; + } + + // First calculate optimal bit lengths + int[] lengths = new int[numNodes]; + lengths[numNodes - 1] = 0; + + for (int i = numNodes - 1; i >= 0; i--) + { + if (childs[2 * i + 1] != -1) + { + int bitLength = lengths[i] + 1; + if (bitLength > maxLength) + { + bitLength = maxLength; + overflow++; + } + lengths[childs[2 * i]] = lengths[childs[2 * i + 1]] = bitLength; + } + else + { + // A leaf node + int bitLength = lengths[i]; + bl_counts[bitLength - 1]++; + this.length[childs[2 * i]] = (byte)lengths[i]; + } + } + + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("Tree "+freqs.Length+" lengths:"); + // for (int i=0; i < numLeafs; i++) { + // //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] + // + " len: "+length[childs[2*i]]); + // } + // } + + if (overflow == 0) + { + return; + } + + int incrBitLen = maxLength - 1; + do + { + // Find the first bit length which could increase: + while (bl_counts[--incrBitLen] == 0) + { + } + + // Move this node one down and remove a corresponding + // number of overflow nodes. + do + { + bl_counts[incrBitLen]--; + bl_counts[++incrBitLen]++; + overflow -= 1 << (maxLength - 1 - incrBitLen); + } while (overflow > 0 && incrBitLen < maxLength - 1); + } while (overflow > 0); + + /* We may have overshot above. Move some nodes from maxLength to + * maxLength-1 in that case. + */ + bl_counts[maxLength - 1] += overflow; + bl_counts[maxLength - 2] -= overflow; + + /* Now recompute all bit lengths, scanning in increasing + * frequency. It is simpler to reconstruct all lengths instead of + * fixing only the wrong ones. This idea is taken from 'ar' + * written by Haruhiko Okumura. + * + * The nodes were inserted with decreasing frequency into the childs + * array. + */ + int nodePtr = 2 * numLeafs; + for (int bits = maxLength; bits != 0; bits--) + { + int n = bl_counts[bits - 1]; + while (n > 0) + { + int childPtr = 2 * childs[nodePtr++]; + if (childs[childPtr + 1] == -1) + { + // We found another leaf + length[childs[childPtr]] = (byte)bits; + n--; + } + } + } + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("*** After overflow elimination. ***"); + // for (int i=0; i < numLeafs; i++) { + // //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] + // + " len: "+length[childs[2*i]]); + // } + // } + } + } + + #region Instance Fields + + /// + /// Pending buffer to use + /// + public DeflaterPending pending; + + private Tree literalTree; + private Tree distTree; + private Tree blTree; + + // Buffer for distances + private short[] d_buf; + + private byte[] l_buf; + private int last_lit; + private int extra_bits; + + #endregion Instance Fields + + static DeflaterHuffman() + { + // See RFC 1951 3.2.6 + // Literal codes + staticLCodes = new short[LITERAL_NUM]; + staticLLength = new byte[LITERAL_NUM]; + + int i = 0; + while (i < 144) + { + staticLCodes[i] = BitReverse((0x030 + i) << 8); + staticLLength[i++] = 8; + } + + while (i < 256) + { + staticLCodes[i] = BitReverse((0x190 - 144 + i) << 7); + staticLLength[i++] = 9; + } + + while (i < 280) + { + staticLCodes[i] = BitReverse((0x000 - 256 + i) << 9); + staticLLength[i++] = 7; + } + + while (i < LITERAL_NUM) + { + staticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8); + staticLLength[i++] = 8; + } + + // Distance codes + staticDCodes = new short[DIST_NUM]; + staticDLength = new byte[DIST_NUM]; + for (i = 0; i < DIST_NUM; i++) + { + staticDCodes[i] = BitReverse(i << 11); + staticDLength[i] = 5; + } + } + + /// + /// Construct instance with pending buffer + /// + /// Pending buffer to use + public DeflaterHuffman(DeflaterPending pending) + { + this.pending = pending; + + literalTree = new Tree(this, LITERAL_NUM, 257, 15); + distTree = new Tree(this, DIST_NUM, 1, 15); + blTree = new Tree(this, BITLEN_NUM, 4, 7); + + d_buf = new short[BUFSIZE]; + l_buf = new byte[BUFSIZE]; + } + + /// + /// Reset internal state + /// + public void Reset() + { + last_lit = 0; + extra_bits = 0; + literalTree.Reset(); + distTree.Reset(); + blTree.Reset(); + } + + /// + /// Write all trees to pending buffer + /// + /// The number/rank of treecodes to send. + public void SendAllTrees(int blTreeCodes) + { + blTree.BuildCodes(); + literalTree.BuildCodes(); + distTree.BuildCodes(); + pending.WriteBits(literalTree.numCodes - 257, 5); + pending.WriteBits(distTree.numCodes - 1, 5); + pending.WriteBits(blTreeCodes - 4, 4); + for (int rank = 0; rank < blTreeCodes; rank++) + { + pending.WriteBits(blTree.length[BL_ORDER[rank]], 3); + } + literalTree.WriteTree(blTree); + distTree.WriteTree(blTree); + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) { + blTree.CheckEmpty(); + } +#endif + } + + /// + /// Compress current buffer writing data to pending buffer + /// + public void CompressBlock() + { + for (int i = 0; i < last_lit; i++) + { + int litlen = l_buf[i] & 0xff; + int dist = d_buf[i]; + if (dist-- != 0) + { + // if (DeflaterConstants.DEBUGGING) { + // Console.Write("["+(dist+1)+","+(litlen+3)+"]: "); + // } + + int lc = Lcode(litlen); + literalTree.WriteSymbol(lc); + + int bits = (lc - 261) / 4; + if (bits > 0 && bits <= 5) + { + pending.WriteBits(litlen & ((1 << bits) - 1), bits); + } + + int dc = Dcode(dist); + distTree.WriteSymbol(dc); + + bits = dc / 2 - 1; + if (bits > 0) + { + pending.WriteBits(dist & ((1 << bits) - 1), bits); + } + } + else + { + // if (DeflaterConstants.DEBUGGING) { + // if (litlen > 32 && litlen < 127) { + // Console.Write("("+(char)litlen+"): "); + // } else { + // Console.Write("{"+litlen+"}: "); + // } + // } + literalTree.WriteSymbol(litlen); + } + } + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) { + Console.Write("EOF: "); + } +#endif + literalTree.WriteSymbol(EOF_SYMBOL); + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) { + literalTree.CheckEmpty(); + distTree.CheckEmpty(); + } +#endif + } + + /// + /// Flush block to output with no compression + /// + /// Data to write + /// Index of first byte to write + /// Count of bytes to write + /// True if this is the last block + public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) + { +#if DebugDeflation + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("Flushing stored block "+ storedLength); + // } +#endif + pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0), 3); + pending.AlignToByte(); + pending.WriteShort(storedLength); + pending.WriteShort(~storedLength); + pending.WriteBlock(stored, storedOffset, storedLength); + Reset(); + } + + /// + /// Flush block to output with compression + /// + /// Data to flush + /// Index of first byte to flush + /// Count of bytes to flush + /// True if this is the last block + public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) + { + literalTree.freqs[EOF_SYMBOL]++; + + // Build trees + literalTree.BuildTree(); + distTree.BuildTree(); + + // Calculate bitlen frequency + literalTree.CalcBLFreq(blTree); + distTree.CalcBLFreq(blTree); + + // Build bitlen tree + blTree.BuildTree(); + + int blTreeCodes = 4; + for (int i = 18; i > blTreeCodes; i--) + { + if (blTree.length[BL_ORDER[i]] > 0) + { + blTreeCodes = i + 1; + } + } + int opt_len = 14 + blTreeCodes * 3 + blTree.GetEncodedLength() + + literalTree.GetEncodedLength() + distTree.GetEncodedLength() + + extra_bits; + + int static_len = extra_bits; + for (int i = 0; i < LITERAL_NUM; i++) + { + static_len += literalTree.freqs[i] * staticLLength[i]; + } + for (int i = 0; i < DIST_NUM; i++) + { + static_len += distTree.freqs[i] * staticDLength[i]; + } + if (opt_len >= static_len) + { + // Force static trees + opt_len = static_len; + } + + if (storedOffset >= 0 && storedLength + 4 < opt_len >> 3) + { + // Store Block + + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("Storing, since " + storedLength + " < " + opt_len + // + " <= " + static_len); + // } + FlushStoredBlock(stored, storedOffset, storedLength, lastBlock); + } + else if (opt_len == static_len) + { + // Encode with static tree + pending.WriteBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0), 3); + literalTree.SetStaticCodes(staticLCodes, staticLLength); + distTree.SetStaticCodes(staticDCodes, staticDLength); + CompressBlock(); + Reset(); + } + else + { + // Encode with dynamic tree + pending.WriteBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0), 3); + SendAllTrees(blTreeCodes); + CompressBlock(); + Reset(); + } + } + + /// + /// Get value indicating if internal buffer is full + /// + /// true if buffer is full + public bool IsFull() + { + return last_lit >= BUFSIZE; + } + + /// + /// Add literal to buffer + /// + /// Literal value to add to buffer. + /// Value indicating internal buffer is full + public bool TallyLit(int literal) + { + // if (DeflaterConstants.DEBUGGING) { + // if (lit > 32 && lit < 127) { + // //Console.WriteLine("("+(char)lit+")"); + // } else { + // //Console.WriteLine("{"+lit+"}"); + // } + // } + d_buf[last_lit] = 0; + l_buf[last_lit++] = (byte)literal; + literalTree.freqs[literal]++; + return IsFull(); + } + + /// + /// Add distance code and length to literal and distance trees + /// + /// Distance code + /// Length + /// Value indicating if internal buffer is full + public bool TallyDist(int distance, int length) + { + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("[" + distance + "," + length + "]"); + // } + + d_buf[last_lit] = (short)distance; + l_buf[last_lit++] = (byte)(length - 3); + + int lc = Lcode(length - 3); + literalTree.freqs[lc]++; + if (lc >= 265 && lc < 285) + { + extra_bits += (lc - 261) / 4; + } + + int dc = Dcode(distance - 1); + distTree.freqs[dc]++; + if (dc >= 4) + { + extra_bits += dc / 2 - 1; + } + return IsFull(); + } + + /// + /// Reverse the bits of a 16 bit value. + /// + /// Value to reverse bits + /// Value with bits reversed + public static short BitReverse(int toReverse) + { + return (short)(bit4Reverse[toReverse & 0xF] << 12 | + bit4Reverse[(toReverse >> 4) & 0xF] << 8 | + bit4Reverse[(toReverse >> 8) & 0xF] << 4 | + bit4Reverse[toReverse >> 12]); + } + + private static int Lcode(int length) + { + if (length == 255) + { + return 285; + } + + int code = 257; + while (length >= 8) + { + code += 4; + length >>= 1; + } + return code + length; + } + + private static int Dcode(int distance) + { + int code = 0; + while (distance >= 4) + { + code += 2; + distance >>= 1; + } + return code + distance; + } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterPending.cs b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterPending.cs new file mode 100644 index 000000000000..80d3e2142e34 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterPending.cs @@ -0,0 +1,17 @@ +namespace ICSharpCode.SharpZipLib.Zip.Compression +{ + /// + /// This class stores the pending output of the Deflater. + /// + /// author of the original java version : Jochen Hoenicke + /// + public class DeflaterPending : PendingBuffer + { + /// + /// Construct instance with default buffer size + /// + public DeflaterPending() : base(DeflaterConstants.PENDING_BUF_SIZE) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Inflater.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Inflater.cs new file mode 100644 index 000000000000..439b4c6010eb --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/Inflater.cs @@ -0,0 +1,887 @@ +using ICSharpCode.SharpZipLib.Checksum; +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; +using System; + +namespace ICSharpCode.SharpZipLib.Zip.Compression +{ + /// + /// Inflater is used to decompress data that has been compressed according + /// to the "deflate" standard described in rfc1951. + /// + /// By default Zlib (rfc1950) headers and footers are expected in the input. + /// You can use constructor public Inflater(bool noHeader) passing true + /// if there is no Zlib header information + /// + /// The usage is as following. First you have to set some input with + /// SetInput(), then Inflate() it. If inflate doesn't + /// inflate any bytes there may be three reasons: + ///
    + ///
  • IsNeedingInput() returns true because the input buffer is empty. + /// You have to provide more input with SetInput(). + /// NOTE: IsNeedingInput() also returns true when, the stream is finished. + ///
  • + ///
  • IsNeedingDictionary() returns true, you have to provide a preset + /// dictionary with SetDictionary().
  • + ///
  • IsFinished returns true, the inflater has finished.
  • + ///
+ /// Once the first output byte is produced, a dictionary will not be + /// needed at a later stage. + /// + /// author of the original java version : John Leuner, Jochen Hoenicke + ///
+ public class Inflater + { + #region Constants/Readonly + + /// + /// Copy lengths for literal codes 257..285 + /// + private static readonly int[] CPLENS = { + 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, 258 + }; + + /// + /// Extra bits for literal codes 257..285 + /// + private static readonly int[] CPLEXT = { + 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 + }; + + /// + /// Copy offsets for distance codes 0..29 + /// + private static readonly int[] CPDIST = { + 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 + }; + + /// + /// Extra bits for distance codes + /// + private static readonly int[] CPDEXT = { + 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 + }; + + /// + /// These are the possible states for an inflater + /// + private const int DECODE_HEADER = 0; + + private const int DECODE_DICT = 1; + private const int DECODE_BLOCKS = 2; + private const int DECODE_STORED_LEN1 = 3; + private const int DECODE_STORED_LEN2 = 4; + private const int DECODE_STORED = 5; + private const int DECODE_DYN_HEADER = 6; + private const int DECODE_HUFFMAN = 7; + private const int DECODE_HUFFMAN_LENBITS = 8; + private const int DECODE_HUFFMAN_DIST = 9; + private const int DECODE_HUFFMAN_DISTBITS = 10; + private const int DECODE_CHKSUM = 11; + private const int FINISHED = 12; + + #endregion Constants/Readonly + + #region Instance Fields + + /// + /// This variable contains the current state. + /// + private int mode; + + /// + /// The adler checksum of the dictionary or of the decompressed + /// stream, as it is written in the header resp. footer of the + /// compressed stream. + /// Only valid if mode is DECODE_DICT or DECODE_CHKSUM. + /// + private int readAdler; + + /// + /// The number of bits needed to complete the current state. This + /// is valid, if mode is DECODE_DICT, DECODE_CHKSUM, + /// DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS. + /// + private int neededBits; + + private int repLength; + private int repDist; + private int uncomprLen; + + /// + /// True, if the last block flag was set in the last block of the + /// inflated stream. This means that the stream ends after the + /// current block. + /// + private bool isLastBlock; + + /// + /// The total number of inflated bytes. + /// + private long totalOut; + + /// + /// The total number of bytes set with setInput(). This is not the + /// value returned by the TotalIn property, since this also includes the + /// unprocessed input. + /// + private long totalIn; + + /// + /// This variable stores the noHeader flag that was given to the constructor. + /// True means, that the inflated stream doesn't contain a Zlib header or + /// footer. + /// + private bool noHeader; + + private readonly StreamManipulator input; + private OutputWindow outputWindow; + private InflaterDynHeader dynHeader; + private InflaterHuffmanTree litlenTree, distTree; + private Adler32 adler; + + #endregion Instance Fields + + #region Constructors + + /// + /// Creates a new inflater or RFC1951 decompressor + /// RFC1950/Zlib headers and footers will be expected in the input data + /// + public Inflater() : this(false) + { + } + + /// + /// Creates a new inflater. + /// + /// + /// True if no RFC1950/Zlib header and footer fields are expected in the input data + /// + /// This is used for GZIPed/Zipped input. + /// + /// For compatibility with + /// Sun JDK you should provide one byte of input more than needed in + /// this case. + /// + public Inflater(bool noHeader) + { + this.noHeader = noHeader; + if (!noHeader) + this.adler = new Adler32(); + input = new StreamManipulator(); + outputWindow = new OutputWindow(); + mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER; + } + + #endregion Constructors + + /// + /// Resets the inflater so that a new stream can be decompressed. All + /// pending input and output will be discarded. + /// + public void Reset() + { + mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER; + totalIn = 0; + totalOut = 0; + input.Reset(); + outputWindow.Reset(); + dynHeader = null; + litlenTree = null; + distTree = null; + isLastBlock = false; + adler?.Reset(); + } + + /// + /// Decodes a zlib/RFC1950 header. + /// + /// + /// False if more input is needed. + /// + /// + /// The header is invalid. + /// + private bool DecodeHeader() + { + int header = input.PeekBits(16); + if (header < 0) + { + return false; + } + input.DropBits(16); + + // The header is written in "wrong" byte order + header = ((header << 8) | (header >> 8)) & 0xffff; + if (header % 31 != 0) + { + throw new SharpZipBaseException("Header checksum illegal"); + } + + if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) + { + throw new SharpZipBaseException("Compression Method unknown"); + } + + /* Maximum size of the backwards window in bits. + * We currently ignore this, but we could use it to make the + * inflater window more space efficient. On the other hand the + * full window (15 bits) is needed most times, anyway. + int max_wbits = ((header & 0x7000) >> 12) + 8; + */ + + if ((header & 0x0020) == 0) + { // Dictionary flag? + mode = DECODE_BLOCKS; + } + else + { + mode = DECODE_DICT; + neededBits = 32; + } + return true; + } + + /// + /// Decodes the dictionary checksum after the deflate header. + /// + /// + /// False if more input is needed. + /// + private bool DecodeDict() + { + while (neededBits > 0) + { + int dictByte = input.PeekBits(8); + if (dictByte < 0) + { + return false; + } + input.DropBits(8); + readAdler = (readAdler << 8) | dictByte; + neededBits -= 8; + } + return false; + } + + /// + /// Decodes the huffman encoded symbols in the input stream. + /// + /// + /// false if more input is needed, true if output window is + /// full or the current block ends. + /// + /// + /// if deflated stream is invalid. + /// + private bool DecodeHuffman() + { + int free = outputWindow.GetFreeSpace(); + while (free >= 258) + { + int symbol; + switch (mode) + { + case DECODE_HUFFMAN: + // This is the inner loop so it is optimized a bit + while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0) + { + outputWindow.Write(symbol); + if (--free < 258) + { + return true; + } + } + + if (symbol < 257) + { + if (symbol < 0) + { + return false; + } + else + { + // symbol == 256: end of block + distTree = null; + litlenTree = null; + mode = DECODE_BLOCKS; + return true; + } + } + + try + { + repLength = CPLENS[symbol - 257]; + neededBits = CPLEXT[symbol - 257]; + } + catch (Exception) + { + throw new SharpZipBaseException("Illegal rep length code"); + } + goto case DECODE_HUFFMAN_LENBITS; // fall through + + case DECODE_HUFFMAN_LENBITS: + if (neededBits > 0) + { + mode = DECODE_HUFFMAN_LENBITS; + int i = input.PeekBits(neededBits); + if (i < 0) + { + return false; + } + input.DropBits(neededBits); + repLength += i; + } + mode = DECODE_HUFFMAN_DIST; + goto case DECODE_HUFFMAN_DIST; // fall through + + case DECODE_HUFFMAN_DIST: + symbol = distTree.GetSymbol(input); + if (symbol < 0) + { + return false; + } + + try + { + repDist = CPDIST[symbol]; + neededBits = CPDEXT[symbol]; + } + catch (Exception) + { + throw new SharpZipBaseException("Illegal rep dist code"); + } + + goto case DECODE_HUFFMAN_DISTBITS; // fall through + + case DECODE_HUFFMAN_DISTBITS: + if (neededBits > 0) + { + mode = DECODE_HUFFMAN_DISTBITS; + int i = input.PeekBits(neededBits); + if (i < 0) + { + return false; + } + input.DropBits(neededBits); + repDist += i; + } + + outputWindow.Repeat(repLength, repDist); + free -= repLength; + mode = DECODE_HUFFMAN; + break; + + default: + throw new SharpZipBaseException("Inflater unknown mode"); + } + } + return true; + } + + /// + /// Decodes the adler checksum after the deflate stream. + /// + /// + /// false if more input is needed. + /// + /// + /// If checksum doesn't match. + /// + private bool DecodeChksum() + { + while (neededBits > 0) + { + int chkByte = input.PeekBits(8); + if (chkByte < 0) + { + return false; + } + input.DropBits(8); + readAdler = (readAdler << 8) | chkByte; + neededBits -= 8; + } + + if ((int)adler?.Value != readAdler) + { + throw new SharpZipBaseException("Adler chksum doesn't match: " + (int)adler?.Value + " vs. " + readAdler); + } + + mode = FINISHED; + return false; + } + + /// + /// Decodes the deflated stream. + /// + /// + /// false if more input is needed, or if finished. + /// + /// + /// if deflated stream is invalid. + /// + private bool Decode() + { + switch (mode) + { + case DECODE_HEADER: + return DecodeHeader(); + + case DECODE_DICT: + return DecodeDict(); + + case DECODE_CHKSUM: + return DecodeChksum(); + + case DECODE_BLOCKS: + if (isLastBlock) + { + if (noHeader) + { + mode = FINISHED; + return false; + } + else + { + input.SkipToByteBoundary(); + neededBits = 32; + mode = DECODE_CHKSUM; + return true; + } + } + + int type = input.PeekBits(3); + if (type < 0) + { + return false; + } + input.DropBits(3); + + isLastBlock |= (type & 1) != 0; + switch (type >> 1) + { + case DeflaterConstants.STORED_BLOCK: + input.SkipToByteBoundary(); + mode = DECODE_STORED_LEN1; + break; + + case DeflaterConstants.STATIC_TREES: + litlenTree = InflaterHuffmanTree.defLitLenTree; + distTree = InflaterHuffmanTree.defDistTree; + mode = DECODE_HUFFMAN; + break; + + case DeflaterConstants.DYN_TREES: + dynHeader = new InflaterDynHeader(input); + mode = DECODE_DYN_HEADER; + break; + + default: + throw new SharpZipBaseException("Unknown block type " + type); + } + return true; + + case DECODE_STORED_LEN1: + { + if ((uncomprLen = input.PeekBits(16)) < 0) + { + return false; + } + input.DropBits(16); + mode = DECODE_STORED_LEN2; + } + goto case DECODE_STORED_LEN2; // fall through + + case DECODE_STORED_LEN2: + { + int nlen = input.PeekBits(16); + if (nlen < 0) + { + return false; + } + input.DropBits(16); + if (nlen != (uncomprLen ^ 0xffff)) + { + throw new SharpZipBaseException("broken uncompressed block"); + } + mode = DECODE_STORED; + } + goto case DECODE_STORED; // fall through + + case DECODE_STORED: + { + int more = outputWindow.CopyStored(input, uncomprLen); + uncomprLen -= more; + if (uncomprLen == 0) + { + mode = DECODE_BLOCKS; + return true; + } + return !input.IsNeedingInput; + } + + case DECODE_DYN_HEADER: + if (!dynHeader.AttemptRead()) + { + return false; + } + + litlenTree = dynHeader.LiteralLengthTree; + distTree = dynHeader.DistanceTree; + mode = DECODE_HUFFMAN; + goto case DECODE_HUFFMAN; // fall through + + case DECODE_HUFFMAN: + case DECODE_HUFFMAN_LENBITS: + case DECODE_HUFFMAN_DIST: + case DECODE_HUFFMAN_DISTBITS: + return DecodeHuffman(); + + case FINISHED: + return false; + + default: + throw new SharpZipBaseException("Inflater.Decode unknown mode"); + } + } + + /// + /// Sets the preset dictionary. This should only be called, if + /// needsDictionary() returns true and it should set the same + /// dictionary, that was used for deflating. The getAdler() + /// function returns the checksum of the dictionary needed. + /// + /// + /// The dictionary. + /// + public void SetDictionary(byte[] buffer) + { + SetDictionary(buffer, 0, buffer.Length); + } + + /// + /// Sets the preset dictionary. This should only be called, if + /// needsDictionary() returns true and it should set the same + /// dictionary, that was used for deflating. The getAdler() + /// function returns the checksum of the dictionary needed. + /// + /// + /// The dictionary. + /// + /// + /// The index into buffer where the dictionary starts. + /// + /// + /// The number of bytes in the dictionary. + /// + /// + /// No dictionary is needed. + /// + /// + /// The adler checksum for the buffer is invalid + /// + public void SetDictionary(byte[] buffer, int index, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (!IsNeedingDictionary) + { + throw new InvalidOperationException("Dictionary is not needed"); + } + + adler?.Update(new ArraySegment(buffer, index, count)); + + if (adler != null && (int)adler.Value != readAdler) + { + throw new SharpZipBaseException("Wrong adler checksum"); + } + adler?.Reset(); + outputWindow.CopyDict(buffer, index, count); + mode = DECODE_BLOCKS; + } + + /// + /// Sets the input. This should only be called, if needsInput() + /// returns true. + /// + /// + /// the input. + /// + public void SetInput(byte[] buffer) + { + SetInput(buffer, 0, buffer.Length); + } + + /// + /// Sets the input. This should only be called, if needsInput() + /// returns true. + /// + /// + /// The source of input data + /// + /// + /// The index into buffer where the input starts. + /// + /// + /// The number of bytes of input to use. + /// + /// + /// No input is needed. + /// + /// + /// The index and/or count are wrong. + /// + public void SetInput(byte[] buffer, int index, int count) + { + input.SetInput(buffer, index, count); + totalIn += (long)count; + } + + /// + /// Inflates the compressed stream to the output buffer. If this + /// returns 0, you should check, whether IsNeedingDictionary(), + /// IsNeedingInput() or IsFinished() returns true, to determine why no + /// further output is produced. + /// + /// + /// the output buffer. + /// + /// + /// The number of bytes written to the buffer, 0 if no further + /// output can be produced. + /// + /// + /// if buffer has length 0. + /// + /// + /// if deflated stream is invalid. + /// + public int Inflate(byte[] buffer) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + return Inflate(buffer, 0, buffer.Length); + } + + /// + /// Inflates the compressed stream to the output buffer. If this + /// returns 0, you should check, whether needsDictionary(), + /// needsInput() or finished() returns true, to determine why no + /// further output is produced. + /// + /// + /// the output buffer. + /// + /// + /// the offset in buffer where storing starts. + /// + /// + /// the maximum number of bytes to output. + /// + /// + /// the number of bytes written to the buffer, 0 if no further output can be produced. + /// + /// + /// if count is less than 0. + /// + /// + /// if the index and / or count are wrong. + /// + /// + /// if deflated stream is invalid. + /// + public int Inflate(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), "count cannot be negative"); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), "offset cannot be negative"); + } + + if (offset + count > buffer.Length) + { + throw new ArgumentException("count exceeds buffer bounds"); + } + + // Special case: count may be zero + if (count == 0) + { + if (!IsFinished) + { // -jr- 08-Nov-2003 INFLATE_BUG fix.. + Decode(); + } + return 0; + } + + int bytesCopied = 0; + + do + { + if (mode != DECODE_CHKSUM) + { + /* Don't give away any output, if we are waiting for the + * checksum in the input stream. + * + * With this trick we have always: + * IsNeedingInput() and not IsFinished() + * implies more output can be produced. + */ + int more = outputWindow.CopyOutput(buffer, offset, count); + if (more > 0) + { + adler?.Update(new ArraySegment(buffer, offset, more)); + offset += more; + bytesCopied += more; + totalOut += (long)more; + count -= more; + if (count == 0) + { + return bytesCopied; + } + } + } + } while (Decode() || ((outputWindow.GetAvailable() > 0) && (mode != DECODE_CHKSUM))); + return bytesCopied; + } + + /// + /// Returns true, if the input buffer is empty. + /// You should then call setInput(). + /// NOTE: This method also returns true when the stream is finished. + /// + public bool IsNeedingInput + { + get + { + return input.IsNeedingInput; + } + } + + /// + /// Returns true, if a preset dictionary is needed to inflate the input. + /// + public bool IsNeedingDictionary + { + get + { + return mode == DECODE_DICT && neededBits == 0; + } + } + + /// + /// Returns true, if the inflater has finished. This means, that no + /// input is needed and no output can be produced. + /// + public bool IsFinished + { + get + { + return mode == FINISHED && outputWindow.GetAvailable() == 0; + } + } + + /// + /// Gets the adler checksum. This is either the checksum of all + /// uncompressed bytes returned by inflate(), or if needsDictionary() + /// returns true (and thus no output was yet produced) this is the + /// adler checksum of the expected dictionary. + /// + /// + /// the adler checksum. + /// + public int Adler + { + get + { + if (IsNeedingDictionary) + { + return readAdler; + } + else if (adler != null) + { + return (int)adler.Value; + } + else + { + return 0; + } + } + } + + /// + /// Gets the total number of output bytes returned by Inflate(). + /// + /// + /// the total number of output bytes. + /// + public long TotalOut + { + get + { + return totalOut; + } + } + + /// + /// Gets the total number of processed compressed input bytes. + /// + /// + /// The total number of bytes of processed input bytes. + /// + public long TotalIn + { + get + { + return totalIn - (long)RemainingInput; + } + } + + /// + /// Gets the number of unprocessed input bytes. Useful, if the end of the + /// stream is reached and you want to further process the bytes after + /// the deflate stream. + /// + /// + /// The number of bytes of the input which have not been processed. + /// + public int RemainingInput + { + // TODO: This should be a long? + get + { + return input.AvailableBytes; + } + } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/InflaterDynHeader.cs b/ICSharpCode.SharpZipLib/Zip/Compression/InflaterDynHeader.cs new file mode 100644 index 000000000000..8e0196b11f71 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/InflaterDynHeader.cs @@ -0,0 +1,151 @@ +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpZipLib.Zip.Compression +{ + internal class InflaterDynHeader + { + #region Constants + + // maximum number of literal/length codes + private const int LITLEN_MAX = 286; + + // maximum number of distance codes + private const int DIST_MAX = 30; + + // maximum data code lengths to read + private const int CODELEN_MAX = LITLEN_MAX + DIST_MAX; + + // maximum meta code length codes to read + private const int META_MAX = 19; + + private static readonly int[] MetaCodeLengthIndex = + { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + #endregion Constants + + /// + /// Continue decoding header from until more bits are needed or decoding has been completed + /// + /// Returns whether decoding could be completed + public bool AttemptRead() + => !state.MoveNext() || state.Current; + + public InflaterDynHeader(StreamManipulator input) + { + this.input = input; + stateMachine = CreateStateMachine(); + state = stateMachine.GetEnumerator(); + } + + private IEnumerable CreateStateMachine() + { + // Read initial code length counts from header + while (!input.TryGetBits(5, ref litLenCodeCount, 257)) yield return false; + while (!input.TryGetBits(5, ref distanceCodeCount, 1)) yield return false; + while (!input.TryGetBits(4, ref metaCodeCount, 4)) yield return false; + var dataCodeCount = litLenCodeCount + distanceCodeCount; + + if (litLenCodeCount > LITLEN_MAX) throw new ValueOutOfRangeException(nameof(litLenCodeCount)); + if (distanceCodeCount > DIST_MAX) throw new ValueOutOfRangeException(nameof(distanceCodeCount)); + if (metaCodeCount > META_MAX) throw new ValueOutOfRangeException(nameof(metaCodeCount)); + + // Load code lengths for the meta tree from the header bits + for (int i = 0; i < metaCodeCount; i++) + { + while (!input.TryGetBits(3, ref codeLengths, MetaCodeLengthIndex[i])) yield return false; + } + + var metaCodeTree = new InflaterHuffmanTree(codeLengths); + + // Decompress the meta tree symbols into the data table code lengths + int index = 0; + while (index < dataCodeCount) + { + byte codeLength; + int symbol; + + while ((symbol = metaCodeTree.GetSymbol(input)) < 0) yield return false; + + if (symbol < 16) + { + // append literal code length + codeLengths[index++] = (byte)symbol; + } + else + { + int repeatCount = 0; + + if (symbol == 16) // Repeat last code length 3..6 times + { + if (index == 0) + throw new StreamDecodingException("Cannot repeat previous code length when no other code length has been read"); + + codeLength = codeLengths[index - 1]; + + // 2 bits + 3, [3..6] + while (!input.TryGetBits(2, ref repeatCount, 3)) yield return false; + } + else if (symbol == 17) // Repeat zero 3..10 times + { + codeLength = 0; + + // 3 bits + 3, [3..10] + while (!input.TryGetBits(3, ref repeatCount, 3)) yield return false; + } + else // (symbol == 18), Repeat zero 11..138 times + { + codeLength = 0; + + // 7 bits + 11, [11..138] + while (!input.TryGetBits(7, ref repeatCount, 11)) yield return false; + } + + if (index + repeatCount > dataCodeCount) + throw new StreamDecodingException("Cannot repeat code lengths past total number of data code lengths"); + + while (repeatCount-- > 0) + codeLengths[index++] = codeLength; + } + } + + if (codeLengths[256] == 0) + throw new StreamDecodingException("Inflater dynamic header end-of-block code missing"); + + litLenTree = new InflaterHuffmanTree(new ArraySegment(codeLengths, 0, litLenCodeCount)); + distTree = new InflaterHuffmanTree(new ArraySegment(codeLengths, litLenCodeCount, distanceCodeCount)); + + yield return true; + } + + /// + /// Get literal/length huffman tree, must not be used before has returned true + /// + /// If hader has not been successfully read by the state machine + public InflaterHuffmanTree LiteralLengthTree + => litLenTree ?? throw new StreamDecodingException("Header properties were accessed before header had been successfully read"); + + /// + /// Get distance huffman tree, must not be used before has returned true + /// + /// If hader has not been successfully read by the state machine + public InflaterHuffmanTree DistanceTree + => distTree ?? throw new StreamDecodingException("Header properties were accessed before header had been successfully read"); + + #region Instance Fields + + private readonly StreamManipulator input; + private readonly IEnumerator state; + private readonly IEnumerable stateMachine; + + private byte[] codeLengths = new byte[CODELEN_MAX]; + + private InflaterHuffmanTree litLenTree; + private InflaterHuffmanTree distTree; + + private int litLenCodeCount, distanceCodeCount, metaCodeCount; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/InflaterHuffmanTree.cs b/ICSharpCode.SharpZipLib/Zip/Compression/InflaterHuffmanTree.cs new file mode 100644 index 000000000000..ed318824ffe3 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/InflaterHuffmanTree.cs @@ -0,0 +1,237 @@ +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpZipLib.Zip.Compression +{ + /// + /// Huffman tree used for inflation + /// + public class InflaterHuffmanTree + { + #region Constants + + private const int MAX_BITLEN = 15; + + #endregion Constants + + #region Instance Fields + + private short[] tree; + + #endregion Instance Fields + + /// + /// Literal length tree + /// + public static InflaterHuffmanTree defLitLenTree; + + /// + /// Distance tree + /// + public static InflaterHuffmanTree defDistTree; + + static InflaterHuffmanTree() + { + try + { + byte[] codeLengths = new byte[288]; + int i = 0; + while (i < 144) + { + codeLengths[i++] = 8; + } + while (i < 256) + { + codeLengths[i++] = 9; + } + while (i < 280) + { + codeLengths[i++] = 7; + } + while (i < 288) + { + codeLengths[i++] = 8; + } + defLitLenTree = new InflaterHuffmanTree(codeLengths); + + codeLengths = new byte[32]; + i = 0; + while (i < 32) + { + codeLengths[i++] = 5; + } + defDistTree = new InflaterHuffmanTree(codeLengths); + } + catch (Exception) + { + throw new SharpZipBaseException("InflaterHuffmanTree: static tree length illegal"); + } + } + + #region Constructors + + /// + /// Constructs a Huffman tree from the array of code lengths. + /// + /// + /// the array of code lengths + /// + public InflaterHuffmanTree(IList codeLengths) + { + BuildTree(codeLengths); + } + + #endregion Constructors + + private void BuildTree(IList codeLengths) + { + int[] blCount = new int[MAX_BITLEN + 1]; + int[] nextCode = new int[MAX_BITLEN + 1]; + + for (int i = 0; i < codeLengths.Count; i++) + { + int bits = codeLengths[i]; + if (bits > 0) + { + blCount[bits]++; + } + } + + int code = 0; + int treeSize = 512; + for (int bits = 1; bits <= MAX_BITLEN; bits++) + { + nextCode[bits] = code; + code += blCount[bits] << (16 - bits); + if (bits >= 10) + { + /* We need an extra table for bit lengths >= 10. */ + int start = nextCode[bits] & 0x1ff80; + int end = code & 0x1ff80; + treeSize += (end - start) >> (16 - bits); + } + } + + /* -jr comment this out! doesnt work for dynamic trees and pkzip 2.04g + if (code != 65536) + { + throw new SharpZipBaseException("Code lengths don't add up properly."); + } + */ + /* Now create and fill the extra tables from longest to shortest + * bit len. This way the sub trees will be aligned. + */ + tree = new short[treeSize]; + int treePtr = 512; + for (int bits = MAX_BITLEN; bits >= 10; bits--) + { + int end = code & 0x1ff80; + code -= blCount[bits] << (16 - bits); + int start = code & 0x1ff80; + for (int i = start; i < end; i += 1 << 7) + { + tree[DeflaterHuffman.BitReverse(i)] = (short)((-treePtr << 4) | bits); + treePtr += 1 << (bits - 9); + } + } + + for (int i = 0; i < codeLengths.Count; i++) + { + int bits = codeLengths[i]; + if (bits == 0) + { + continue; + } + code = nextCode[bits]; + int revcode = DeflaterHuffman.BitReverse(code); + if (bits <= 9) + { + do + { + tree[revcode] = (short)((i << 4) | bits); + revcode += 1 << bits; + } while (revcode < 512); + } + else + { + int subTree = tree[revcode & 511]; + int treeLen = 1 << (subTree & 15); + subTree = -(subTree >> 4); + do + { + tree[subTree | (revcode >> 9)] = (short)((i << 4) | bits); + revcode += 1 << bits; + } while (revcode < treeLen); + } + nextCode[bits] = code + (1 << (16 - bits)); + } + } + + /// + /// Reads the next symbol from input. The symbol is encoded using the + /// huffman tree. + /// + /// + /// input the input source. + /// + /// + /// the next symbol, or -1 if not enough input is available. + /// + public int GetSymbol(StreamManipulator input) + { + int lookahead, symbol; + if ((lookahead = input.PeekBits(9)) >= 0) + { + symbol = tree[lookahead]; + int bitlen = symbol & 15; + + if (symbol >= 0) + { + if(bitlen == 0){ + throw new SharpZipBaseException("Encountered invalid codelength 0"); + } + input.DropBits(bitlen); + return symbol >> 4; + } + int subtree = -(symbol >> 4); + if ((lookahead = input.PeekBits(bitlen)) >= 0) + { + symbol = tree[subtree | (lookahead >> 9)]; + input.DropBits(symbol & 15); + return symbol >> 4; + } + else + { + int bits = input.AvailableBits; + lookahead = input.PeekBits(bits); + symbol = tree[subtree | (lookahead >> 9)]; + if ((symbol & 15) <= bits) + { + input.DropBits(symbol & 15); + return symbol >> 4; + } + else + { + return -1; + } + } + } + else // Less than 9 bits + { + int bits = input.AvailableBits; + lookahead = input.PeekBits(bits); + symbol = tree[lookahead]; + if (symbol >= 0 && (symbol & 15) <= bits) + { + input.DropBits(symbol & 15); + return symbol >> 4; + } + else + { + return -1; + } + } + } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/PendingBuffer.cs b/ICSharpCode.SharpZipLib/Zip/Compression/PendingBuffer.cs new file mode 100644 index 000000000000..6ed7e4ab8a72 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/PendingBuffer.cs @@ -0,0 +1,268 @@ +namespace ICSharpCode.SharpZipLib.Zip.Compression +{ + /// + /// This class is general purpose class for writing data to a buffer. + /// + /// It allows you to write bits as well as bytes + /// Based on DeflaterPending.java + /// + /// author of the original java version : Jochen Hoenicke + /// + public class PendingBuffer + { + #region Instance Fields + + /// + /// Internal work buffer + /// + private readonly byte[] buffer; + + private int start; + private int end; + + private uint bits; + private int bitCount; + + #endregion Instance Fields + + #region Constructors + + /// + /// construct instance using default buffer size of 4096 + /// + public PendingBuffer() : this(4096) + { + } + + /// + /// construct instance using specified buffer size + /// + /// + /// size to use for internal buffer + /// + public PendingBuffer(int bufferSize) + { + buffer = new byte[bufferSize]; + } + + #endregion Constructors + + /// + /// Clear internal state/buffers + /// + public void Reset() + { + start = end = bitCount = 0; + } + + /// + /// Write a byte to buffer + /// + /// + /// The value to write + /// + public void WriteByte(int value) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + buffer[end++] = unchecked((byte)value); + } + + /// + /// Write a short value to buffer LSB first + /// + /// + /// The value to write. + /// + public void WriteShort(int value) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + buffer[end++] = unchecked((byte)value); + buffer[end++] = unchecked((byte)(value >> 8)); + } + + /// + /// write an integer LSB first + /// + /// The value to write. + public void WriteInt(int value) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + buffer[end++] = unchecked((byte)value); + buffer[end++] = unchecked((byte)(value >> 8)); + buffer[end++] = unchecked((byte)(value >> 16)); + buffer[end++] = unchecked((byte)(value >> 24)); + } + + /// + /// Write a block of data to buffer + /// + /// data to write + /// offset of first byte to write + /// number of bytes to write + public void WriteBlock(byte[] block, int offset, int length) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + System.Array.Copy(block, offset, buffer, end, length); + end += length; + } + + /// + /// The number of bits written to the buffer + /// + public int BitCount + { + get + { + return bitCount; + } + } + + /// + /// Align internal buffer on a byte boundary + /// + public void AlignToByte() + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + if (bitCount > 0) + { + buffer[end++] = unchecked((byte)bits); + if (bitCount > 8) + { + buffer[end++] = unchecked((byte)(bits >> 8)); + } + } + bits = 0; + bitCount = 0; + } + + /// + /// Write bits to internal buffer + /// + /// source of bits + /// number of bits to write + public void WriteBits(int b, int count) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } + + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("writeBits("+b+","+count+")"); + // } +#endif + bits |= (uint)(b << bitCount); + bitCount += count; + if (bitCount >= 16) + { + buffer[end++] = unchecked((byte)bits); + buffer[end++] = unchecked((byte)(bits >> 8)); + bits >>= 16; + bitCount -= 16; + } + } + + /// + /// Write a short value to internal buffer most significant byte first + /// + /// value to write + public void WriteShortMSB(int s) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + buffer[end++] = unchecked((byte)(s >> 8)); + buffer[end++] = unchecked((byte)s); + } + + /// + /// Indicates if buffer has been flushed + /// + public bool IsFlushed + { + get + { + return end == 0; + } + } + + /// + /// Flushes the pending buffer into the given output array. If the + /// output array is to small, only a partial flush is done. + /// + /// The output array. + /// The offset into output array. + /// The maximum number of bytes to store. + /// The number of bytes flushed. + public int Flush(byte[] output, int offset, int length) + { + if (bitCount >= 8) + { + buffer[end++] = unchecked((byte)bits); + bits >>= 8; + bitCount -= 8; + } + + if (length > end - start) + { + length = end - start; + System.Array.Copy(buffer, start, output, offset, length); + start = 0; + end = 0; + } + else + { + System.Array.Copy(buffer, start, output, offset, length); + start += length; + } + return length; + } + + /// + /// Convert internal buffer to byte array. + /// Buffer is empty on completion + /// + /// + /// The internal buffer contents converted to a byte array. + /// + public byte[] ToByteArray() + { + AlignToByte(); + + byte[] result = new byte[end - start]; + System.Array.Copy(buffer, start, result, 0, result.Length); + start = 0; + end = 0; + return result; + } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs new file mode 100644 index 000000000000..1c54b6848076 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs @@ -0,0 +1,513 @@ +using ICSharpCode.SharpZipLib.Encryption; +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams +{ + /// + /// A special stream deflating or compressing the bytes that are + /// written to it. It uses a Deflater to perform actual deflating.
+ /// Authors of the original java version : Tom Tromey, Jochen Hoenicke + ///
+ public class DeflaterOutputStream : Stream + { + #region Constructors + + /// + /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size. + /// + /// + /// the output stream where deflated output should be written. + /// + public DeflaterOutputStream(Stream baseOutputStream) + : this(baseOutputStream, new Deflater(), 512) + { + } + + /// + /// Creates a new DeflaterOutputStream with the given Deflater and + /// default buffer size. + /// + /// + /// the output stream where deflated output should be written. + /// + /// + /// the underlying deflater. + /// + public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater) + : this(baseOutputStream, deflater, 512) + { + } + + /// + /// Creates a new DeflaterOutputStream with the given Deflater and + /// buffer size. + /// + /// + /// The output stream where deflated output is written. + /// + /// + /// The underlying deflater to use + /// + /// + /// The buffer size in bytes to use when deflating (minimum value 512) + /// + /// + /// bufsize is less than or equal to zero. + /// + /// + /// baseOutputStream does not support writing + /// + /// + /// deflater instance is null + /// + public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize) + { + if (baseOutputStream == null) + { + throw new ArgumentNullException(nameof(baseOutputStream)); + } + + if (baseOutputStream.CanWrite == false) + { + throw new ArgumentException("Must support writing", nameof(baseOutputStream)); + } + + if (bufferSize < 512) + { + throw new ArgumentOutOfRangeException(nameof(bufferSize)); + } + + baseOutputStream_ = baseOutputStream; + buffer_ = new byte[bufferSize]; + deflater_ = deflater ?? throw new ArgumentNullException(nameof(deflater)); + } + + #endregion Constructors + + #region Public API + + /// + /// Finishes the stream by calling finish() on the deflater. + /// + /// + /// Not all input is deflated + /// + public virtual void Finish() + { + deflater_.Finish(); + while (!deflater_.IsFinished) + { + int len = deflater_.Deflate(buffer_, 0, buffer_.Length); + if (len <= 0) + { + break; + } + + EncryptBlock(buffer_, 0, len); + + baseOutputStream_.Write(buffer_, 0, len); + } + + if (!deflater_.IsFinished) + { + throw new SharpZipBaseException("Can't deflate all input?"); + } + + baseOutputStream_.Flush(); + + if (cryptoTransform_ != null) + { + if (cryptoTransform_ is ZipAESTransform) + { + AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); + } + cryptoTransform_.Dispose(); + cryptoTransform_ = null; + } + } + + /// + /// Finishes the stream by calling finish() on the deflater. + /// + /// The that can be used to cancel the operation. + /// + /// Not all input is deflated + /// + public virtual async Task FinishAsync(CancellationToken ct) + { + deflater_.Finish(); + while (!deflater_.IsFinished) + { + int len = deflater_.Deflate(buffer_, 0, buffer_.Length); + if (len <= 0) + { + break; + } + + EncryptBlock(buffer_, 0, len); + + await baseOutputStream_.WriteAsync(buffer_, 0, len, ct); + } + + if (!deflater_.IsFinished) + { + throw new SharpZipBaseException("Can't deflate all input?"); + } + + await baseOutputStream_.FlushAsync(ct); + + if (cryptoTransform_ != null) + { + if (cryptoTransform_ is ZipAESTransform) + { + AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); + } + cryptoTransform_.Dispose(); + cryptoTransform_ = null; + } + } + + /// + /// Gets or sets a flag indicating ownership of underlying stream. + /// When the flag is true will close the underlying stream also. + /// + /// The default value is true. + public bool IsStreamOwner { get; set; } = true; + + /// + /// Allows client to determine if an entry can be patched after its added + /// + public bool CanPatchEntries + { + get + { + return baseOutputStream_.CanSeek; + } + } + + #endregion Public API + + #region Encryption + + /// + /// The CryptoTransform currently being used to encrypt the compressed data. + /// + protected ICryptoTransform cryptoTransform_; + + /// + /// Returns the 10 byte AUTH CODE to be appended immediately following the AES data stream. + /// + protected byte[] AESAuthCode; + + /// + public Encoding ZipCryptoEncoding { get; set; } = StringCodec.DefaultZipCryptoEncoding; + + /// + /// Encrypt a block of data + /// + /// + /// Data to encrypt. NOTE the original contents of the buffer are lost + /// + /// + /// Offset of first byte in buffer to encrypt + /// + /// + /// Number of bytes in buffer to encrypt + /// + protected void EncryptBlock(byte[] buffer, int offset, int length) + { + if(cryptoTransform_ is null) return; + cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0); + } + + #endregion Encryption + + #region Deflation Support + + /// + /// Deflates everything in the input buffers. This will call + /// def.deflate() until all bytes from the input buffers + /// are processed. + /// + protected void Deflate() + { + Deflate(false); + } + + private void Deflate(bool flushing) + { + while (flushing || !deflater_.IsNeedingInput) + { + int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length); + + if (deflateCount <= 0) + { + break; + } + + EncryptBlock(buffer_, 0, deflateCount); + + baseOutputStream_.Write(buffer_, 0, deflateCount); + } + + if (!deflater_.IsNeedingInput) + { + throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?"); + } + } + + #endregion Deflation Support + + #region Stream Overrides + + /// + /// Gets value indicating stream can be read from + /// + public override bool CanRead + { + get + { + return false; + } + } + + /// + /// Gets a value indicating if seeking is supported for this stream + /// This property always returns false + /// + public override bool CanSeek + { + get + { + return false; + } + } + + /// + /// Get value indicating if this stream supports writing + /// + public override bool CanWrite + { + get + { + return baseOutputStream_.CanWrite; + } + } + + /// + /// Get current length of stream + /// + public override long Length + { + get + { + return baseOutputStream_.Length; + } + } + + /// + /// Gets the current position within the stream. + /// + /// Any attempt to set position + public override long Position + { + get + { + return baseOutputStream_.Position; + } + set + { + throw new NotSupportedException("Position property not supported"); + } + } + + /// + /// Sets the current position of this stream to the given value. Not supported by this class! + /// + /// The offset relative to the to seek. + /// The to seek from. + /// The new position in the stream. + /// Any access + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("DeflaterOutputStream Seek not supported"); + } + + /// + /// Sets the length of this stream to the given value. Not supported by this class! + /// + /// The new stream length. + /// Any access + public override void SetLength(long value) + { + throw new NotSupportedException("DeflaterOutputStream SetLength not supported"); + } + + /// + /// Read a byte from stream advancing position by one + /// + /// The byte read cast to an int. THe value is -1 if at the end of the stream. + /// Any access + public override int ReadByte() + { + throw new NotSupportedException("DeflaterOutputStream ReadByte not supported"); + } + + /// + /// Read a block of bytes from stream + /// + /// The buffer to store read data in. + /// The offset to start storing at. + /// The maximum number of bytes to read. + /// The actual number of bytes read. Zero if end of stream is detected. + /// Any access + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException("DeflaterOutputStream Read not supported"); + } + + /// + /// Flushes the stream by calling Flush on the deflater and then + /// on the underlying stream. This ensures that all bytes are flushed. + /// + public override void Flush() + { + deflater_.Flush(); + Deflate(true); + baseOutputStream_.Flush(); + } + + /// + /// Calls and closes the underlying + /// stream when is true. + /// + protected override void Dispose(bool disposing) + { + if (!isClosed_) + { + isClosed_ = true; + + try + { + Finish(); + if (cryptoTransform_ != null) + { + GetAuthCodeIfAES(); + cryptoTransform_.Dispose(); + cryptoTransform_ = null; + } + } + finally + { + if (IsStreamOwner) + { + baseOutputStream_.Dispose(); + } + } + } + } + +#if NETSTANDARD2_1 + /// + /// Calls and closes the underlying + /// stream when is true. + /// + public override async ValueTask DisposeAsync() + { + if (!isClosed_) + { + isClosed_ = true; + + try + { + await FinishAsync(CancellationToken.None); + if (cryptoTransform_ != null) + { + GetAuthCodeIfAES(); + cryptoTransform_.Dispose(); + cryptoTransform_ = null; + } + } + finally + { + if (IsStreamOwner) + { + await baseOutputStream_.DisposeAsync(); + } + } + } + } +#endif + + /// + /// Get the Auth code for AES encrypted entries + /// + protected void GetAuthCodeIfAES() + { + if (cryptoTransform_ is ZipAESTransform) + { + AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); + } + } + + /// + /// Writes a single byte to the compressed output stream. + /// + /// + /// The byte value. + /// + public override void WriteByte(byte value) + { + byte[] b = new byte[1]; + b[0] = value; + Write(b, 0, 1); + } + + /// + /// Writes bytes from an array to the compressed stream. + /// + /// + /// The byte array + /// + /// + /// The offset into the byte array where to start. + /// + /// + /// The number of bytes to write. + /// + public override void Write(byte[] buffer, int offset, int count) + { + deflater_.SetInput(buffer, offset, count); + Deflate(); + } + + #endregion Stream Overrides + + #region Instance Fields + + /// + /// This buffer is used temporarily to retrieve the bytes from the + /// deflater and write them to the underlying output stream. + /// + private byte[] buffer_; + + /// + /// The deflater which is used to deflate the stream. + /// + protected Deflater deflater_; + + /// + /// Base stream the deflater depends on. + /// + protected Stream baseOutputStream_; + + private bool isClosed_; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs new file mode 100644 index 000000000000..7790474d24a6 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs @@ -0,0 +1,713 @@ +using System; +using System.IO; +using System.Security.Cryptography; + +namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams +{ + /// + /// An input buffer customised for use by + /// + /// + /// The buffer supports decryption of incoming data. + /// + public class InflaterInputBuffer + { + #region Constructors + + /// + /// Initialise a new instance of with a default buffer size + /// + /// The stream to buffer. + public InflaterInputBuffer(Stream stream) : this(stream, 4096) + { + } + + /// + /// Initialise a new instance of + /// + /// The stream to buffer. + /// The size to use for the buffer + /// A minimum buffer size of 1KB is permitted. Lower sizes are treated as 1KB. + public InflaterInputBuffer(Stream stream, int bufferSize) + { + inputStream = stream; + if (bufferSize < 1024) + { + bufferSize = 1024; + } + rawData = new byte[bufferSize]; + clearText = rawData; + } + + #endregion Constructors + + /// + /// Get the length of bytes in the + /// + public int RawLength + { + get + { + return rawLength; + } + } + + /// + /// Get the contents of the raw data buffer. + /// + /// This may contain encrypted data. + public byte[] RawData + { + get + { + return rawData; + } + } + + /// + /// Get the number of useable bytes in + /// + public int ClearTextLength + { + get + { + return clearTextLength; + } + } + + /// + /// Get the contents of the clear text buffer. + /// + public byte[] ClearText + { + get + { + return clearText; + } + } + + /// + /// Get/set the number of bytes available + /// + public int Available + { + get { return available; } + set { available = value; } + } + + /// + /// Call passing the current clear text buffer contents. + /// + /// The inflater to set input for. + public void SetInflaterInput(Inflater inflater) + { + if (available > 0) + { + inflater.SetInput(clearText, clearTextLength - available, available); + available = 0; + } + } + + /// + /// Fill the buffer from the underlying input stream. + /// + public void Fill() + { + rawLength = 0; + int toRead = rawData.Length; + + while (toRead > 0 && inputStream.CanRead) + { + int count = inputStream.Read(rawData, rawLength, toRead); + if (count <= 0) + { + break; + } + rawLength += count; + toRead -= count; + } + + if (cryptoTransform != null) + { + clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0); + } + else + { + clearTextLength = rawLength; + } + + available = clearTextLength; + } + + /// + /// Read a buffer directly from the input stream + /// + /// The buffer to fill + /// Returns the number of bytes read. + public int ReadRawBuffer(byte[] buffer) + { + return ReadRawBuffer(buffer, 0, buffer.Length); + } + + /// + /// Read a buffer directly from the input stream + /// + /// The buffer to read into + /// The offset to start reading data into. + /// The number of bytes to read. + /// Returns the number of bytes read. + public int ReadRawBuffer(byte[] outBuffer, int offset, int length) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + int currentOffset = offset; + int currentLength = length; + + while (currentLength > 0) + { + if (available <= 0) + { + Fill(); + if (available <= 0) + { + return 0; + } + } + int toCopy = Math.Min(currentLength, available); + System.Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy); + currentOffset += toCopy; + currentLength -= toCopy; + available -= toCopy; + } + return length; + } + + /// + /// Read clear text data from the input stream. + /// + /// The buffer to add data to. + /// The offset to start adding data at. + /// The number of bytes to read. + /// Returns the number of bytes actually read. + public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + int currentOffset = offset; + int currentLength = length; + + while (currentLength > 0) + { + if (available <= 0) + { + Fill(); + if (available <= 0) + { + return 0; + } + } + + int toCopy = Math.Min(currentLength, available); + Array.Copy(clearText, clearTextLength - (int)available, outBuffer, currentOffset, toCopy); + currentOffset += toCopy; + currentLength -= toCopy; + available -= toCopy; + } + return length; + } + + /// + /// Read a from the input stream. + /// + /// Returns the byte read. + public byte ReadLeByte() + { + if (available <= 0) + { + Fill(); + if (available <= 0) + { + throw new ZipException("EOF in header"); + } + } + byte result = rawData[rawLength - available]; + available -= 1; + return result; + } + + /// + /// Read an in little endian byte order. + /// + /// The short value read case to an int. + public int ReadLeShort() + { + return ReadLeByte() | (ReadLeByte() << 8); + } + + /// + /// Read an in little endian byte order. + /// + /// The int value read. + public int ReadLeInt() + { + return ReadLeShort() | (ReadLeShort() << 16); + } + + /// + /// Read a in little endian byte order. + /// + /// The long value read. + public long ReadLeLong() + { + return (uint)ReadLeInt() | ((long)ReadLeInt() << 32); + } + + /// + /// Get/set the to apply to any data. + /// + /// Set this value to null to have no transform applied. + public ICryptoTransform CryptoTransform + { + set + { + cryptoTransform = value; + if (cryptoTransform != null) + { + if (rawData == clearText) + { + if (internalClearText == null) + { + internalClearText = new byte[rawData.Length]; + } + clearText = internalClearText; + } + clearTextLength = rawLength; + if (available > 0) + { + cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available); + } + } + else + { + clearText = rawData; + clearTextLength = rawLength; + } + } + } + + #region Instance Fields + + private int rawLength; + private byte[] rawData; + + private int clearTextLength; + private byte[] clearText; + private byte[] internalClearText; + + private int available; + + private ICryptoTransform cryptoTransform; + private Stream inputStream; + + #endregion Instance Fields + } + + /// + /// This filter stream is used to decompress data compressed using the "deflate" + /// format. The "deflate" format is described in RFC 1951. + /// + /// This stream may form the basis for other decompression filters, such + /// as the GZipInputStream. + /// + /// Author of the original java version : John Leuner. + /// + public class InflaterInputStream : Stream + { + #region Constructors + + /// + /// Create an InflaterInputStream with the default decompressor + /// and a default buffer size of 4KB. + /// + /// + /// The InputStream to read bytes from + /// + public InflaterInputStream(Stream baseInputStream) + : this(baseInputStream, new Inflater(), 4096) + { + } + + /// + /// Create an InflaterInputStream with the specified decompressor + /// and a default buffer size of 4KB. + /// + /// + /// The source of input data + /// + /// + /// The decompressor used to decompress data read from baseInputStream + /// + public InflaterInputStream(Stream baseInputStream, Inflater inf) + : this(baseInputStream, inf, 4096) + { + } + + /// + /// Create an InflaterInputStream with the specified decompressor + /// and the specified buffer size. + /// + /// + /// The InputStream to read bytes from + /// + /// + /// The decompressor to use + /// + /// + /// Size of the buffer to use + /// + public InflaterInputStream(Stream baseInputStream, Inflater inflater, int bufferSize) + { + if (baseInputStream == null) + { + throw new ArgumentNullException(nameof(baseInputStream)); + } + + if (inflater == null) + { + throw new ArgumentNullException(nameof(inflater)); + } + + if (bufferSize <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bufferSize)); + } + + this.baseInputStream = baseInputStream; + this.inf = inflater; + + inputBuffer = new InflaterInputBuffer(baseInputStream, bufferSize); + } + + #endregion Constructors + + /// + /// Gets or sets a flag indicating ownership of underlying stream. + /// When the flag is true will close the underlying stream also. + /// + /// The default value is true. + public bool IsStreamOwner { get; set; } = true; + + /// + /// Skip specified number of bytes of uncompressed data + /// + /// + /// Number of bytes to skip + /// + /// + /// The number of bytes skipped, zero if the end of + /// stream has been reached + /// + /// + /// The number of bytes to skip is less than or equal to zero. + /// + public long Skip(long count) + { + if (count <= 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + // v0.80 Skip by seeking if underlying stream supports it... + if (baseInputStream.CanSeek) + { + baseInputStream.Seek(count, SeekOrigin.Current); + return count; + } + else + { + int length = 2048; + if (count < length) + { + length = (int)count; + } + + byte[] tmp = new byte[length]; + int readCount = 1; + long toSkip = count; + + while ((toSkip > 0) && (readCount > 0)) + { + if (toSkip < length) + { + length = (int)toSkip; + } + + readCount = baseInputStream.Read(tmp, 0, length); + toSkip -= readCount; + } + + return count - toSkip; + } + } + + /// + /// Clear any cryptographic state. + /// + protected void StopDecrypting() + { + inputBuffer.CryptoTransform = null; + } + + /// + /// Returns 0 once the end of the stream (EOF) has been reached. + /// Otherwise returns 1. + /// + public virtual int Available + { + get + { + return inf.IsFinished ? 0 : 1; + } + } + + /// + /// Fills the buffer with more data to decompress. + /// + /// + /// Stream ends early + /// + protected void Fill() + { + // Protect against redundant calls + if (inputBuffer.Available <= 0) + { + inputBuffer.Fill(); + if (inputBuffer.Available <= 0) + { + throw new SharpZipBaseException("Unexpected EOF"); + } + } + inputBuffer.SetInflaterInput(inf); + } + + #region Stream Overrides + + /// + /// Gets a value indicating whether the current stream supports reading + /// + public override bool CanRead + { + get + { + return baseInputStream.CanRead; + } + } + + /// + /// Gets a value of false indicating seeking is not supported for this stream. + /// + public override bool CanSeek + { + get + { + return false; + } + } + + /// + /// Gets a value of false indicating that this stream is not writeable. + /// + public override bool CanWrite + { + get + { + return false; + } + } + + /// + /// A value representing the length of the stream in bytes. + /// + public override long Length + { + get + { + //return inputBuffer.RawLength; + throw new NotSupportedException("InflaterInputStream Length is not supported"); + } + } + + /// + /// The current position within the stream. + /// Throws a NotSupportedException when attempting to set the position + /// + /// Attempting to set the position + public override long Position + { + get + { + return baseInputStream.Position; + } + set + { + throw new NotSupportedException("InflaterInputStream Position not supported"); + } + } + + /// + /// Flushes the baseInputStream + /// + public override void Flush() + { + baseInputStream.Flush(); + } + + /// + /// Sets the position within the current stream + /// Always throws a NotSupportedException + /// + /// The relative offset to seek to. + /// The defining where to seek from. + /// The new position in the stream. + /// Any access + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("Seek not supported"); + } + + /// + /// Set the length of the current stream + /// Always throws a NotSupportedException + /// + /// The new length value for the stream. + /// Any access + public override void SetLength(long value) + { + throw new NotSupportedException("InflaterInputStream SetLength not supported"); + } + + /// + /// Writes a sequence of bytes to stream and advances the current position + /// This method always throws a NotSupportedException + /// + /// The buffer containing data to write. + /// The offset of the first byte to write. + /// The number of bytes to write. + /// Any access + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException("InflaterInputStream Write not supported"); + } + + /// + /// Writes one byte to the current stream and advances the current position + /// Always throws a NotSupportedException + /// + /// The byte to write. + /// Any access + public override void WriteByte(byte value) + { + throw new NotSupportedException("InflaterInputStream WriteByte not supported"); + } + + /// + /// Closes the input stream. When + /// is true the underlying stream is also closed. + /// + protected override void Dispose(bool disposing) + { + if (!isClosed) + { + isClosed = true; + if (IsStreamOwner) + { + baseInputStream.Dispose(); + } + } + } + + /// + /// Reads decompressed data into the provided buffer byte array + /// + /// + /// The array to read and decompress data into + /// + /// + /// The offset indicating where the data should be placed + /// + /// + /// The number of bytes to decompress + /// + /// The number of bytes read. Zero signals the end of stream + /// + /// Inflater needs a dictionary + /// + public override int Read(byte[] buffer, int offset, int count) + { + if (inf.IsNeedingDictionary) + { + throw new SharpZipBaseException("Need a dictionary"); + } + + int remainingBytes = count; + while (true) + { + int bytesRead = inf.Inflate(buffer, offset, remainingBytes); + offset += bytesRead; + remainingBytes -= bytesRead; + + if (remainingBytes == 0 || inf.IsFinished) + { + break; + } + + if (inf.IsNeedingInput) + { + Fill(); + } + else if (bytesRead == 0) + { + throw new ZipException("Invalid input data"); + } + } + return count - remainingBytes; + } + + #endregion Stream Overrides + + #region Instance Fields + + /// + /// Decompressor for this stream + /// + protected Inflater inf; + + /// + /// Input buffer for this stream. + /// + protected InflaterInputBuffer inputBuffer; + + /// + /// Base stream the inflater reads from. + /// + private Stream baseInputStream; + + /// + /// The compressed size + /// + protected long csize; + + /// + /// Flag indicating whether this instance has been closed or not. + /// + private bool isClosed; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/OutputWindow.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/OutputWindow.cs new file mode 100644 index 000000000000..d8241c18c200 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/OutputWindow.cs @@ -0,0 +1,220 @@ +using System; + +namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams +{ + /// + /// Contains the output from the Inflation process. + /// We need to have a window so that we can refer backwards into the output stream + /// to repeat stuff.
+ /// Author of the original java version : John Leuner + ///
+ public class OutputWindow + { + #region Constants + + private const int WindowSize = 1 << 15; + private const int WindowMask = WindowSize - 1; + + #endregion Constants + + #region Instance Fields + + private byte[] window = new byte[WindowSize]; //The window is 2^15 bytes + private int windowEnd; + private int windowFilled; + + #endregion Instance Fields + + /// + /// Write a byte to this output window + /// + /// value to write + /// + /// if window is full + /// + public void Write(int value) + { + if (windowFilled++ == WindowSize) + { + throw new InvalidOperationException("Window full"); + } + window[windowEnd++] = (byte)value; + windowEnd &= WindowMask; + } + + private void SlowRepeat(int repStart, int length, int distance) + { + while (length-- > 0) + { + window[windowEnd++] = window[repStart++]; + windowEnd &= WindowMask; + repStart &= WindowMask; + } + } + + /// + /// Append a byte pattern already in the window itself + /// + /// length of pattern to copy + /// distance from end of window pattern occurs + /// + /// If the repeated data overflows the window + /// + public void Repeat(int length, int distance) + { + if ((windowFilled += length) > WindowSize) + { + throw new InvalidOperationException("Window full"); + } + + int repStart = (windowEnd - distance) & WindowMask; + int border = WindowSize - length; + if ((repStart <= border) && (windowEnd < border)) + { + if (length <= distance) + { + System.Array.Copy(window, repStart, window, windowEnd, length); + windowEnd += length; + } + else + { + // We have to copy manually, since the repeat pattern overlaps. + while (length-- > 0) + { + window[windowEnd++] = window[repStart++]; + } + } + } + else + { + SlowRepeat(repStart, length, distance); + } + } + + /// + /// Copy from input manipulator to internal window + /// + /// source of data + /// length of data to copy + /// the number of bytes copied + public int CopyStored(StreamManipulator input, int length) + { + length = Math.Min(Math.Min(length, WindowSize - windowFilled), input.AvailableBytes); + int copied; + + int tailLen = WindowSize - windowEnd; + if (length > tailLen) + { + copied = input.CopyBytes(window, windowEnd, tailLen); + if (copied == tailLen) + { + copied += input.CopyBytes(window, 0, length - tailLen); + } + } + else + { + copied = input.CopyBytes(window, windowEnd, length); + } + + windowEnd = (windowEnd + copied) & WindowMask; + windowFilled += copied; + return copied; + } + + /// + /// Copy dictionary to window + /// + /// source dictionary + /// offset of start in source dictionary + /// length of dictionary + /// + /// If window isnt empty + /// + public void CopyDict(byte[] dictionary, int offset, int length) + { + if (dictionary == null) + { + throw new ArgumentNullException(nameof(dictionary)); + } + + if (windowFilled > 0) + { + throw new InvalidOperationException(); + } + + if (length > WindowSize) + { + offset += length - WindowSize; + length = WindowSize; + } + System.Array.Copy(dictionary, offset, window, 0, length); + windowEnd = length & WindowMask; + } + + /// + /// Get remaining unfilled space in window + /// + /// Number of bytes left in window + public int GetFreeSpace() + { + return WindowSize - windowFilled; + } + + /// + /// Get bytes available for output in window + /// + /// Number of bytes filled + public int GetAvailable() + { + return windowFilled; + } + + /// + /// Copy contents of window to output + /// + /// buffer to copy to + /// offset to start at + /// number of bytes to count + /// The number of bytes copied + /// + /// If a window underflow occurs + /// + public int CopyOutput(byte[] output, int offset, int len) + { + int copyEnd = windowEnd; + if (len > windowFilled) + { + len = windowFilled; + } + else + { + copyEnd = (windowEnd - windowFilled + len) & WindowMask; + } + + int copied = len; + int tailLen = len - copyEnd; + + if (tailLen > 0) + { + System.Array.Copy(window, WindowSize - tailLen, output, offset, tailLen); + offset += tailLen; + len = copyEnd; + } + System.Array.Copy(window, copyEnd - len, output, offset, len); + windowFilled -= copied; + if (windowFilled < 0) + { + throw new InvalidOperationException(); + } + return copied; + } + + /// + /// Reset by clearing window so GetAvailable returns 0 + /// + public void Reset() + { + windowFilled = windowEnd = 0; + } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/StreamManipulator.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/StreamManipulator.cs new file mode 100644 index 000000000000..aff6a9c6c525 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/StreamManipulator.cs @@ -0,0 +1,298 @@ +using System; + +namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams +{ + /// + /// This class allows us to retrieve a specified number of bits from + /// the input buffer, as well as copy big byte blocks. + /// + /// It uses an int buffer to store up to 31 bits for direct + /// manipulation. This guarantees that we can get at least 16 bits, + /// but we only need at most 15, so this is all safe. + /// + /// There are some optimizations in this class, for example, you must + /// never peek more than 8 bits more than needed, and you must first + /// peek bits before you may drop them. This is not a general purpose + /// class but optimized for the behaviour of the Inflater. + /// + /// authors of the original java version : John Leuner, Jochen Hoenicke + /// + public class StreamManipulator + { + /// + /// Get the next sequence of bits but don't increase input pointer. bitCount must be + /// less or equal 16 and if this call succeeds, you must drop + /// at least n - 8 bits in the next call. + /// + /// The number of bits to peek. + /// + /// the value of the bits, or -1 if not enough bits available. */ + /// + public int PeekBits(int bitCount) + { + if (bitsInBuffer_ < bitCount) + { + if (windowStart_ == windowEnd_) + { + return -1; // ok + } + buffer_ |= (uint)((window_[windowStart_++] & 0xff | + (window_[windowStart_++] & 0xff) << 8) << bitsInBuffer_); + bitsInBuffer_ += 16; + } + return (int)(buffer_ & ((1 << bitCount) - 1)); + } + + /// + /// Tries to grab the next bits from the input and + /// sets to the value, adding . + /// + /// true if enough bits could be read, otherwise false + public bool TryGetBits(int bitCount, ref int output, int outputOffset = 0) + { + var bits = PeekBits(bitCount); + if (bits < 0) + { + return false; + } + output = bits + outputOffset; + DropBits(bitCount); + return true; + } + + /// + /// Tries to grab the next bits from the input and + /// sets of to the value. + /// + /// true if enough bits could be read, otherwise false + public bool TryGetBits(int bitCount, ref byte[] array, int index) + { + var bits = PeekBits(bitCount); + if (bits < 0) + { + return false; + } + array[index] = (byte)bits; + DropBits(bitCount); + return true; + } + + /// + /// Drops the next n bits from the input. You should have called PeekBits + /// with a bigger or equal n before, to make sure that enough bits are in + /// the bit buffer. + /// + /// The number of bits to drop. + public void DropBits(int bitCount) + { + buffer_ >>= bitCount; + bitsInBuffer_ -= bitCount; + } + + /// + /// Gets the next n bits and increases input pointer. This is equivalent + /// to followed by , except for correct error handling. + /// + /// The number of bits to retrieve. + /// + /// the value of the bits, or -1 if not enough bits available. + /// + public int GetBits(int bitCount) + { + int bits = PeekBits(bitCount); + if (bits >= 0) + { + DropBits(bitCount); + } + return bits; + } + + /// + /// Gets the number of bits available in the bit buffer. This must be + /// only called when a previous PeekBits() returned -1. + /// + /// + /// the number of bits available. + /// + public int AvailableBits + { + get + { + return bitsInBuffer_; + } + } + + /// + /// Gets the number of bytes available. + /// + /// + /// The number of bytes available. + /// + public int AvailableBytes + { + get + { + return windowEnd_ - windowStart_ + (bitsInBuffer_ >> 3); + } + } + + /// + /// Skips to the next byte boundary. + /// + public void SkipToByteBoundary() + { + buffer_ >>= (bitsInBuffer_ & 7); + bitsInBuffer_ &= ~7; + } + + /// + /// Returns true when SetInput can be called + /// + public bool IsNeedingInput + { + get + { + return windowStart_ == windowEnd_; + } + } + + /// + /// Copies 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. + /// + /// + /// The buffer to copy bytes to. + /// + /// + /// The offset in the buffer at which copying starts + /// + /// + /// The length to copy, 0 is allowed. + /// + /// + /// The number of bytes copied, 0 if no bytes were available. + /// + /// + /// Length is less than zero + /// + /// + /// Bit buffer isnt byte aligned + /// + public int CopyBytes(byte[] output, int offset, int length) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + if ((bitsInBuffer_ & 7) != 0) + { + // bits_in_buffer may only be 0 or a multiple of 8 + throw new InvalidOperationException("Bit buffer is not byte aligned!"); + } + + int count = 0; + while ((bitsInBuffer_ > 0) && (length > 0)) + { + output[offset++] = (byte)buffer_; + buffer_ >>= 8; + bitsInBuffer_ -= 8; + length--; + count++; + } + + if (length == 0) + { + return count; + } + + int avail = windowEnd_ - windowStart_; + if (length > avail) + { + length = avail; + } + System.Array.Copy(window_, windowStart_, output, offset, length); + windowStart_ += length; + + if (((windowStart_ - windowEnd_) & 1) != 0) + { + // We always want an even number of bytes in input, see peekBits + buffer_ = (uint)(window_[windowStart_++] & 0xff); + bitsInBuffer_ = 8; + } + return count + length; + } + + /// + /// Resets state and empties internal buffers + /// + public void Reset() + { + buffer_ = 0; + windowStart_ = windowEnd_ = bitsInBuffer_ = 0; + } + + /// + /// Add more input for consumption. + /// Only call when IsNeedingInput returns true + /// + /// data to be input + /// offset of first byte of input + /// number of bytes of input to add. + public void SetInput(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative"); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative"); + } + + if (windowStart_ < windowEnd_) + { + throw new InvalidOperationException("Old input was not completely processed"); + } + + int end = offset + count; + + // We want to throw an ArrayIndexOutOfBoundsException early. + // Note the check also handles integer wrap around. + if ((offset > end) || (end > buffer.Length)) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if ((count & 1) != 0) + { + // We always want an even number of bytes in input, see PeekBits + buffer_ |= (uint)((buffer[offset++] & 0xff) << bitsInBuffer_); + bitsInBuffer_ += 8; + } + + window_ = buffer; + windowStart_ = offset; + windowEnd_ = end; + } + + #region Instance Fields + + private byte[] window_; + private int windowStart_; + private int windowEnd_; + + private uint buffer_; + private int bitsInBuffer_; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/FastZip.cs b/ICSharpCode.SharpZipLib/Zip/FastZip.cs new file mode 100644 index 000000000000..13aedb02101d --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/FastZip.cs @@ -0,0 +1,1003 @@ +using ICSharpCode.SharpZipLib.Core; +using ICSharpCode.SharpZipLib.Zip.Compression; +using System; +using System.IO; +using static ICSharpCode.SharpZipLib.Zip.Compression.Deflater; +using static ICSharpCode.SharpZipLib.Zip.ZipEntryFactory; + +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// FastZipEvents supports all events applicable to FastZip operations. + /// + public class FastZipEvents + { + /// + /// Delegate to invoke when processing directories. + /// + public event EventHandler ProcessDirectory; + + /// + /// Delegate to invoke when processing files. + /// + public ProcessFileHandler ProcessFile; + + /// + /// Delegate to invoke during processing of files. + /// + public ProgressHandler Progress; + + /// + /// Delegate to invoke when processing for a file has been completed. + /// + public CompletedFileHandler CompletedFile; + + /// + /// Delegate to invoke when processing directory failures. + /// + public DirectoryFailureHandler DirectoryFailure; + + /// + /// Delegate to invoke when processing file failures. + /// + public FileFailureHandler FileFailure; + + /// + /// Raise the directory failure event. + /// + /// The directory causing the failure. + /// The exception for this event. + /// A boolean indicating if execution should continue or not. + public bool OnDirectoryFailure(string directory, Exception e) + { + bool result = false; + DirectoryFailureHandler handler = DirectoryFailure; + + if (handler != null) + { + var args = new ScanFailureEventArgs(directory, e); + handler(this, args); + result = args.ContinueRunning; + } + return result; + } + + /// + /// Fires the file failure handler delegate. + /// + /// The file causing the failure. + /// The exception for this failure. + /// A boolean indicating if execution should continue or not. + public bool OnFileFailure(string file, Exception e) + { + FileFailureHandler handler = FileFailure; + bool result = (handler != null); + + if (result) + { + var args = new ScanFailureEventArgs(file, e); + handler(this, args); + result = args.ContinueRunning; + } + return result; + } + + /// + /// Fires the ProcessFile delegate. + /// + /// The file being processed. + /// A boolean indicating if execution should continue or not. + public bool OnProcessFile(string file) + { + bool result = true; + ProcessFileHandler handler = ProcessFile; + + if (handler != null) + { + var args = new ScanEventArgs(file); + handler(this, args); + result = args.ContinueRunning; + } + return result; + } + + /// + /// Fires the delegate + /// + /// The file whose processing has been completed. + /// A boolean indicating if execution should continue or not. + public bool OnCompletedFile(string file) + { + bool result = true; + CompletedFileHandler handler = CompletedFile; + if (handler != null) + { + var args = new ScanEventArgs(file); + handler(this, args); + result = args.ContinueRunning; + } + return result; + } + + /// + /// Fires the process directory delegate. + /// + /// The directory being processed. + /// Flag indicating if the directory has matching files as determined by the current filter. + /// A of true if the operation should continue; false otherwise. + public bool OnProcessDirectory(string directory, bool hasMatchingFiles) + { + bool result = true; + EventHandler handler = ProcessDirectory; + if (handler != null) + { + var args = new DirectoryEventArgs(directory, hasMatchingFiles); + handler(this, args); + result = args.ContinueRunning; + } + return result; + } + + /// + /// The minimum timespan between events. + /// + /// The minimum period of time between events. + /// + /// The default interval is three seconds. + public TimeSpan ProgressInterval + { + get { return progressInterval_; } + set { progressInterval_ = value; } + } + + #region Instance Fields + + private TimeSpan progressInterval_ = TimeSpan.FromSeconds(3); + + #endregion Instance Fields + } + + /// + /// FastZip provides facilities for creating and extracting zip files. + /// + public class FastZip + { + #region Enumerations + + /// + /// Defines the desired handling when overwriting files during extraction. + /// + public enum Overwrite + { + /// + /// Prompt the user to confirm overwriting + /// + Prompt, + + /// + /// Never overwrite files. + /// + Never, + + /// + /// Always overwrite files. + /// + Always + } + + #endregion Enumerations + + #region Constructors + + /// + /// Initialise a default instance of . + /// + public FastZip() + { + } + + /// + /// Initialise a new instance of using the specified + /// + /// The time setting to use when creating or extracting Zip entries. + /// Using TimeSetting.LastAccessTime[Utc] when + /// creating an archive will set the file time to the moment of reading. + /// + public FastZip(TimeSetting timeSetting) + { + entryFactory_ = new ZipEntryFactory(timeSetting); + restoreDateTimeOnExtract_ = true; + } + + /// + /// Initialise a new instance of using the specified + /// + /// The time to set all values for created or extracted Zip Entries. + public FastZip(DateTime time) + { + entryFactory_ = new ZipEntryFactory(time); + restoreDateTimeOnExtract_ = true; + } + + /// + /// Initialise a new instance of + /// + /// The events to use during operations. + public FastZip(FastZipEvents events) + { + events_ = events; + } + + #endregion Constructors + + #region Properties + + /// + /// Get/set a value indicating whether empty directories should be created. + /// + public bool CreateEmptyDirectories + { + get { return createEmptyDirectories_; } + set { createEmptyDirectories_ = value; } + } + + /// + /// Get / set the password value. + /// + public string Password + { + get { return password_; } + set { password_ = value; } + } + + /// + /// Get / set the method of encrypting entries. + /// + /// + /// Only applies when is set. + /// Defaults to ZipCrypto for backwards compatibility purposes. + /// + public ZipEncryptionMethod EntryEncryptionMethod { get; set; } = ZipEncryptionMethod.ZipCrypto; + + /// + /// Get or set the active when creating Zip files. + /// + /// + public INameTransform NameTransform + { + get { return entryFactory_.NameTransform; } + set + { + entryFactory_.NameTransform = value; + } + } + + /// + /// Get or set the active when creating Zip files. + /// + public IEntryFactory EntryFactory + { + get { return entryFactory_; } + set + { + if (value == null) + { + entryFactory_ = new ZipEntryFactory(); + } + else + { + entryFactory_ = value; + } + } + } + + /// + /// Gets or sets the setting for Zip64 handling when writing. + /// + /// + /// The default value is dynamic which is not backwards compatible with old + /// programs and can cause problems with XP's built in compression which cant + /// read Zip64 archives. However it does avoid the situation were a large file + /// is added and cannot be completed correctly. + /// NOTE: Setting the size for entries before they are added is the best solution! + /// By default the EntryFactory used by FastZip will set the file size. + /// + public UseZip64 UseZip64 + { + get { return useZip64_; } + set { useZip64_ = value; } + } + + /// + /// Get/set a value indicating whether file dates and times should + /// be restored when extracting files from an archive. + /// + /// The default value is false. + public bool RestoreDateTimeOnExtract + { + get + { + return restoreDateTimeOnExtract_; + } + set + { + restoreDateTimeOnExtract_ = value; + } + } + + /// + /// Get/set a value indicating whether file attributes should + /// be restored during extract operations + /// + public bool RestoreAttributesOnExtract + { + get { return restoreAttributesOnExtract_; } + set { restoreAttributesOnExtract_ = value; } + } + + /// + /// Get/set the Compression Level that will be used + /// when creating the zip + /// + public Deflater.CompressionLevel CompressionLevel + { + get { return compressionLevel_; } + set { compressionLevel_ = value; } + } + + /// + /// Reflects the opposite of the internal , setting it to false overrides the encoding used for reading and writing zip entries + /// + public bool UseUnicode + { + get => !_stringCodec.ForceZipLegacyEncoding; + set => _stringCodec.ForceZipLegacyEncoding = !value; + } + + /// Gets or sets the code page used for reading/writing zip file entries when unicode is disabled + public int LegacyCodePage + { + get => _stringCodec.CodePage; + set => _stringCodec.CodePage = value; + } + + /// + public StringCodec StringCodec + { + get => _stringCodec; + set => _stringCodec = value; + } + + #endregion Properties + + #region Delegates + + /// + /// Delegate called when confirming overwriting of files. + /// + public delegate bool ConfirmOverwriteDelegate(string fileName); + + #endregion Delegates + + #region CreateZip + + /// + /// Create a zip file. + /// + /// The name of the zip file to create. + /// The directory to source files from. + /// True to recurse directories, false for no recursion. + /// The file filter to apply. + /// The directory filter to apply. + public void CreateZip(string zipFileName, string sourceDirectory, + bool recurse, string fileFilter, string directoryFilter) + { + CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, directoryFilter); + } + + /// + /// Create a zip file/archive. + /// + /// The name of the zip file to create. + /// The directory to obtain files and directories from. + /// True to recurse directories, false for no recursion. + /// The file filter to apply. + public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter) + { + CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, null); + } + + /// + /// Create a zip archive sending output to the passed. + /// + /// The stream to write archive data to. + /// The directory to source files from. + /// True to recurse directories, false for no recursion. + /// The file filter to apply. + /// The directory filter to apply. + /// The is closed after creation. + public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter) + { + CreateZip(outputStream, sourceDirectory, recurse, fileFilter, directoryFilter, false); + } + + /// + /// Create a zip archive sending output to the passed. + /// + /// The stream to write archive data to. + /// The directory to source files from. + /// True to recurse directories, false for no recursion. + /// The file filter to apply. + /// The directory filter to apply. + /// true to leave open after the zip has been created, false to dispose it. + public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter, bool leaveOpen) + { + var scanner = new FileSystemScanner(fileFilter, directoryFilter); + CreateZip(outputStream, sourceDirectory, recurse, scanner, leaveOpen); + } + + /// + /// Create a zip file. + /// + /// The name of the zip file to create. + /// The directory to source files from. + /// True to recurse directories, false for no recursion. + /// The file filter to apply. + /// The directory filter to apply. + public void CreateZip(string zipFileName, string sourceDirectory, + bool recurse, IScanFilter fileFilter, IScanFilter directoryFilter) + { + CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, directoryFilter, false); + } + + /// + /// Create a zip archive sending output to the passed. + /// + /// The stream to write archive data to. + /// The directory to source files from. + /// True to recurse directories, false for no recursion. + /// The file filter to apply. + /// The directory filter to apply. + /// true to leave open after the zip has been created, false to dispose it. + public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, IScanFilter fileFilter, IScanFilter directoryFilter, bool leaveOpen = false) + { + var scanner = new FileSystemScanner(fileFilter, directoryFilter); + CreateZip(outputStream, sourceDirectory, recurse, scanner, leaveOpen); + } + + /// + /// Create a zip archive sending output to the passed. + /// + /// The stream to write archive data to. + /// The directory to source files from. + /// True to recurse directories, false for no recursion. + /// For performing the actual file system scan + /// true to leave open after the zip has been created, false to dispose it. + /// The is closed after creation. + private void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, FileSystemScanner scanner, bool leaveOpen) + { + NameTransform = new ZipNameTransform(sourceDirectory); + sourceDirectory_ = sourceDirectory; + + using (outputStream_ = new ZipOutputStream(outputStream, _stringCodec)) + { + outputStream_.SetLevel((int)CompressionLevel); + outputStream_.IsStreamOwner = !leaveOpen; + outputStream_.NameTransform = null; // all required transforms handled by us + + if (false == string.IsNullOrEmpty(password_) && EntryEncryptionMethod != ZipEncryptionMethod.None) + { + outputStream_.Password = password_; + } + + outputStream_.UseZip64 = UseZip64; + scanner.ProcessFile += ProcessFile; + if (this.CreateEmptyDirectories) + { + scanner.ProcessDirectory += ProcessDirectory; + } + + if (events_ != null) + { + if (events_.FileFailure != null) + { + scanner.FileFailure += events_.FileFailure; + } + + if (events_.DirectoryFailure != null) + { + scanner.DirectoryFailure += events_.DirectoryFailure; + } + } + + scanner.Scan(sourceDirectory, recurse); + } + } + + #endregion CreateZip + + #region ExtractZip + + /// + /// Extract the contents of a zip file. + /// + /// The zip file to extract from. + /// The directory to save extracted information in. + /// A filter to apply to files. + public void ExtractZip(string zipFileName, string targetDirectory, string fileFilter) + { + ExtractZip(zipFileName, targetDirectory, Overwrite.Always, null, fileFilter, null, restoreDateTimeOnExtract_); + } + + /// + /// Extract the contents of a zip file. + /// + /// The zip file to extract from. + /// The directory to save extracted information in. + /// The style of overwriting to apply. + /// A delegate to invoke when confirming overwriting. + /// A filter to apply to files. + /// A filter to apply to directories. + /// Flag indicating whether to restore the date and time for extracted files. + /// Allow parent directory traversal in file paths (e.g. ../file) + public void ExtractZip(string zipFileName, string targetDirectory, + Overwrite overwrite, ConfirmOverwriteDelegate confirmDelegate, + string fileFilter, string directoryFilter, bool restoreDateTime, bool allowParentTraversal = false) + { + Stream inputStream = File.Open(zipFileName, FileMode.Open, FileAccess.Read, FileShare.Read); + ExtractZip(inputStream, targetDirectory, overwrite, confirmDelegate, fileFilter, directoryFilter, restoreDateTime, true, allowParentTraversal); + } + + /// + /// Extract the contents of a zip file held in a stream. + /// + /// The seekable input stream containing the zip to extract from. + /// The directory to save extracted information in. + /// The style of overwriting to apply. + /// A delegate to invoke when confirming overwriting. + /// A filter to apply to files. + /// A filter to apply to directories. + /// Flag indicating whether to restore the date and time for extracted files. + /// Flag indicating whether the inputStream will be closed by this method. + /// Allow parent directory traversal in file paths (e.g. ../file) + public void ExtractZip(Stream inputStream, string targetDirectory, + Overwrite overwrite, ConfirmOverwriteDelegate confirmDelegate, + string fileFilter, string directoryFilter, bool restoreDateTime, + bool isStreamOwner, bool allowParentTraversal = false) + { + if ((overwrite == Overwrite.Prompt) && (confirmDelegate == null)) + { + throw new ArgumentNullException(nameof(confirmDelegate)); + } + + continueRunning_ = true; + overwrite_ = overwrite; + confirmDelegate_ = confirmDelegate; + extractNameTransform_ = new WindowsNameTransform(targetDirectory, allowParentTraversal); + + fileFilter_ = new NameFilter(fileFilter); + directoryFilter_ = new NameFilter(directoryFilter); + restoreDateTimeOnExtract_ = restoreDateTime; + + using (zipFile_ = new ZipFile(inputStream, !isStreamOwner)) + { + if (password_ != null) + { + zipFile_.Password = password_; + } + + System.Collections.IEnumerator enumerator = zipFile_.GetEnumerator(); + while (continueRunning_ && enumerator.MoveNext()) + { + var entry = (ZipEntry)enumerator.Current; + if (entry.IsFile) + { + // TODO Path.GetDirectory can fail here on invalid characters. + if (directoryFilter_.IsMatch(Path.GetDirectoryName(entry.Name)) && fileFilter_.IsMatch(entry.Name)) + { + ExtractEntry(entry); + } + } + else if (entry.IsDirectory) + { + if (directoryFilter_.IsMatch(entry.Name) && CreateEmptyDirectories) + { + ExtractEntry(entry); + } + } + else + { + // Do nothing for volume labels etc... + } + } + } + } + + #endregion ExtractZip + + #region Internal Processing + + private void ProcessDirectory(object sender, DirectoryEventArgs e) + { + if (!e.HasMatchingFiles && CreateEmptyDirectories) + { + if (events_ != null) + { + events_.OnProcessDirectory(e.Name, e.HasMatchingFiles); + } + + if (e.ContinueRunning) + { + if (e.Name != sourceDirectory_) + { + ZipEntry entry = entryFactory_.MakeDirectoryEntry(e.Name); + outputStream_.PutNextEntry(entry); + } + } + } + } + + private void ProcessFile(object sender, ScanEventArgs e) + { + if ((events_ != null) && (events_.ProcessFile != null)) + { + events_.ProcessFile(sender, e); + } + + if (e.ContinueRunning) + { + try + { + // The open below is equivalent to OpenRead which guarantees that if opened the + // file will not be changed by subsequent openers, but precludes opening in some cases + // were it could succeed. ie the open may fail as its already open for writing and the share mode should reflect that. + using (FileStream stream = File.Open(e.Name, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + ZipEntry entry = entryFactory_.MakeFileEntry(e.Name); + if (_stringCodec.ForceZipLegacyEncoding) + { + entry.IsUnicodeText = false; + } + + // Set up AES encryption for the entry if required. + ConfigureEntryEncryption(entry); + + outputStream_.PutNextEntry(entry); + AddFileContents(e.Name, stream); + } + } + catch (Exception ex) + { + if (events_ != null) + { + continueRunning_ = events_.OnFileFailure(e.Name, ex); + } + else + { + continueRunning_ = false; + throw; + } + } + } + } + + // Set up the encryption method to use for the specific entry. + private void ConfigureEntryEncryption(ZipEntry entry) + { + // Only alter the entries options if AES isn't already enabled for it + // (it might have been set up by the entry factory, and if so we let that take precedence) + if (!string.IsNullOrEmpty(Password) && entry.AESEncryptionStrength == 0) + { + switch (EntryEncryptionMethod) + { + case ZipEncryptionMethod.AES128: + entry.AESKeySize = 128; + break; + + case ZipEncryptionMethod.AES256: + entry.AESKeySize = 256; + break; + } + } + } + + private void AddFileContents(string name, Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (buffer_ == null) + { + buffer_ = new byte[4096]; + } + + if ((events_ != null) && (events_.Progress != null)) + { + StreamUtils.Copy(stream, outputStream_, buffer_, + events_.Progress, events_.ProgressInterval, this, name); + } + else + { + StreamUtils.Copy(stream, outputStream_, buffer_); + } + + if (events_ != null) + { + continueRunning_ = events_.OnCompletedFile(name); + } + } + + private void ExtractFileEntry(ZipEntry entry, string targetName) + { + bool proceed = true; + if (overwrite_ != Overwrite.Always) + { + if (File.Exists(targetName)) + { + if ((overwrite_ == Overwrite.Prompt) && (confirmDelegate_ != null)) + { + proceed = confirmDelegate_(targetName); + } + else + { + proceed = false; + } + } + } + + if (proceed) + { + if (events_ != null) + { + continueRunning_ = events_.OnProcessFile(entry.Name); + } + + if (continueRunning_) + { + try + { + using (FileStream outputStream = File.Create(targetName)) + { + if (buffer_ == null) + { + buffer_ = new byte[4096]; + } + + using (var inputStream = zipFile_.GetInputStream(entry)) + { + if ((events_ != null) && (events_.Progress != null)) + { + StreamUtils.Copy(inputStream, outputStream, buffer_, + events_.Progress, events_.ProgressInterval, this, entry.Name, entry.Size); + } + else + { + StreamUtils.Copy(inputStream, outputStream, buffer_); + } + } + + if (events_ != null) + { + continueRunning_ = events_.OnCompletedFile(entry.Name); + } + } + + if (restoreDateTimeOnExtract_) + { + switch (entryFactory_.Setting) + { + case TimeSetting.CreateTime: + File.SetCreationTime(targetName, entry.DateTime); + break; + + case TimeSetting.CreateTimeUtc: + File.SetCreationTimeUtc(targetName, entry.DateTime); + break; + + case TimeSetting.LastAccessTime: + File.SetLastAccessTime(targetName, entry.DateTime); + break; + + case TimeSetting.LastAccessTimeUtc: + File.SetLastAccessTimeUtc(targetName, entry.DateTime); + break; + + case TimeSetting.LastWriteTime: + File.SetLastWriteTime(targetName, entry.DateTime); + break; + + case TimeSetting.LastWriteTimeUtc: + File.SetLastWriteTimeUtc(targetName, entry.DateTime); + break; + + case TimeSetting.Fixed: + File.SetLastWriteTime(targetName, entryFactory_.FixedDateTime); + break; + + default: + throw new ZipException("Unhandled time setting in ExtractFileEntry"); + } + } + + if (RestoreAttributesOnExtract && entry.IsDOSEntry && (entry.ExternalFileAttributes != -1)) + { + var fileAttributes = (FileAttributes)entry.ExternalFileAttributes; + // TODO: FastZip - Setting of other file attributes on extraction is a little trickier. + fileAttributes &= (FileAttributes.Archive | FileAttributes.Normal | FileAttributes.ReadOnly | FileAttributes.Hidden); + File.SetAttributes(targetName, fileAttributes); + } + } + catch (Exception ex) + { + if (events_ != null) + { + continueRunning_ = events_.OnFileFailure(targetName, ex); + } + else + { + continueRunning_ = false; + throw; + } + } + } + } + } + + private void ExtractEntry(ZipEntry entry) + { + bool doExtraction = entry.IsCompressionMethodSupported(); + string targetName = entry.Name; + + if (doExtraction) + { + if (entry.IsFile) + { + targetName = extractNameTransform_.TransformFile(targetName); + } + else if (entry.IsDirectory) + { + targetName = extractNameTransform_.TransformDirectory(targetName); + } + + doExtraction = !(string.IsNullOrEmpty(targetName)); + } + + // TODO: Fire delegate/throw exception were compression method not supported, or name is invalid? + + string dirName = string.Empty; + + if (doExtraction) + { + if (entry.IsDirectory) + { + dirName = targetName; + } + else + { + dirName = Path.GetDirectoryName(Path.GetFullPath(targetName)); + } + } + + if (doExtraction && !Directory.Exists(dirName)) + { + if (!entry.IsDirectory || CreateEmptyDirectories) + { + try + { + continueRunning_ = events_?.OnProcessDirectory(dirName, true) ?? true; + if (continueRunning_) + { + Directory.CreateDirectory(dirName); + if (entry.IsDirectory && restoreDateTimeOnExtract_) + { + switch (entryFactory_.Setting) + { + case TimeSetting.CreateTime: + Directory.SetCreationTime(dirName, entry.DateTime); + break; + + case TimeSetting.CreateTimeUtc: + Directory.SetCreationTimeUtc(dirName, entry.DateTime); + break; + + case TimeSetting.LastAccessTime: + Directory.SetLastAccessTime(dirName, entry.DateTime); + break; + + case TimeSetting.LastAccessTimeUtc: + Directory.SetLastAccessTimeUtc(dirName, entry.DateTime); + break; + + case TimeSetting.LastWriteTime: + Directory.SetLastWriteTime(dirName, entry.DateTime); + break; + + case TimeSetting.LastWriteTimeUtc: + Directory.SetLastWriteTimeUtc(dirName, entry.DateTime); + break; + + case TimeSetting.Fixed: + Directory.SetLastWriteTime(dirName, entryFactory_.FixedDateTime); + break; + + default: + throw new ZipException("Unhandled time setting in ExtractEntry"); + } + } + } + else + { + doExtraction = false; + } + } + catch (Exception ex) + { + doExtraction = false; + if (events_ != null) + { + if (entry.IsDirectory) + { + continueRunning_ = events_.OnDirectoryFailure(targetName, ex); + } + else + { + continueRunning_ = events_.OnFileFailure(targetName, ex); + } + } + else + { + continueRunning_ = false; + throw; + } + } + } + } + + if (doExtraction && entry.IsFile) + { + ExtractFileEntry(entry, targetName); + } + } + + private static int MakeExternalAttributes(FileInfo info) + { + return (int)info.Attributes; + } + + private static bool NameIsValid(string name) + { + return !string.IsNullOrEmpty(name) && + (name.IndexOfAny(Path.GetInvalidPathChars()) < 0); + } + + #endregion Internal Processing + + #region Instance Fields + + private bool continueRunning_; + private byte[] buffer_; + private ZipOutputStream outputStream_; + private ZipFile zipFile_; + private string sourceDirectory_; + private NameFilter fileFilter_; + private NameFilter directoryFilter_; + private Overwrite overwrite_; + private ConfirmOverwriteDelegate confirmDelegate_; + + private bool restoreDateTimeOnExtract_; + private bool restoreAttributesOnExtract_; + private bool createEmptyDirectories_; + private FastZipEvents events_; + private IEntryFactory entryFactory_ = new ZipEntryFactory(); + private INameTransform extractNameTransform_; + private UseZip64 useZip64_ = UseZip64.Dynamic; + private CompressionLevel compressionLevel_ = CompressionLevel.DEFAULT_COMPRESSION; + private StringCodec _stringCodec = new StringCodec(); + + private string password_; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs b/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs new file mode 100644 index 000000000000..d7ec181405ef --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs @@ -0,0 +1,67 @@ +using System; +using ICSharpCode.SharpZipLib.Core; +using static ICSharpCode.SharpZipLib.Zip.ZipEntryFactory; + +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// Defines factory methods for creating new values. + /// + public interface IEntryFactory + { + /// + /// Create a for a file given its name + /// + /// The name of the file to create an entry for. + /// Returns a file entry based on the passed. + ZipEntry MakeFileEntry(string fileName); + + /// + /// Create a for a file given its name + /// + /// The name of the file to create an entry for. + /// If true get details from the file system if the file exists. + /// Returns a file entry based on the passed. + ZipEntry MakeFileEntry(string fileName, bool useFileSystem); + + /// + /// Create a for a file given its actual name and optional override name + /// + /// The name of the file to create an entry for. + /// An alternative name to be used for the new entry. Null if not applicable. + /// If true get details from the file system if the file exists. + /// Returns a file entry based on the passed. + ZipEntry MakeFileEntry(string fileName, string entryName, bool useFileSystem); + + /// + /// Create a for a directory given its name + /// + /// The name of the directory to create an entry for. + /// Returns a directory entry based on the passed. + ZipEntry MakeDirectoryEntry(string directoryName); + + /// + /// Create a for a directory given its name + /// + /// The name of the directory to create an entry for. + /// If true get details from the file system for this directory if it exists. + /// Returns a directory entry based on the passed. + ZipEntry MakeDirectoryEntry(string directoryName, bool useFileSystem); + + /// + /// Get/set the applicable. + /// + INameTransform NameTransform { get; set; } + + /// + /// Get the in use. + /// + TimeSetting Setting { get; } + + /// + /// Get the value to use when is set to , + /// or if not specified, the value of when the class was the initialized + /// + DateTime FixedDateTime { get; } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/WindowsNameTransform.cs b/ICSharpCode.SharpZipLib/Zip/WindowsNameTransform.cs new file mode 100644 index 000000000000..43aa61403634 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/WindowsNameTransform.cs @@ -0,0 +1,266 @@ +using ICSharpCode.SharpZipLib.Core; +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// WindowsNameTransform transforms names to windows compatible ones. + /// + public class WindowsNameTransform : INameTransform + { + /// + /// The maximum windows path name permitted. + /// + /// This may not valid for all windows systems - CE?, etc but I cant find the equivalent in the CLR. + private const int MaxPath = 260; + + private string _baseDirectory; + private bool _trimIncomingPaths; + private char _replacementChar = '_'; + private bool _allowParentTraversal; + + /// + /// In this case we need Windows' invalid path characters. + /// Path.GetInvalidPathChars() only returns a subset invalid on all platforms. + /// + private static readonly char[] InvalidEntryChars = new char[] { + '"', '<', '>', '|', '\0', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', + '\u0006', '\a', '\b', '\t', '\n', '\v', '\f', '\r', '\u000e', '\u000f', + '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015', '\u0016', + '\u0017', '\u0018', '\u0019', '\u001a', '\u001b', '\u001c', '\u001d', + '\u001e', '\u001f', + // extra characters for masks, etc. + '*', '?', ':' + }; + + /// + /// Initialises a new instance of + /// + /// + /// Allow parent directory traversal in file paths (e.g. ../file) + public WindowsNameTransform(string baseDirectory, bool allowParentTraversal = false) + { + BaseDirectory = baseDirectory ?? throw new ArgumentNullException(nameof(baseDirectory), "Directory name is invalid"); + AllowParentTraversal = allowParentTraversal; + } + + /// + /// Initialise a default instance of + /// + public WindowsNameTransform() + { + // Do nothing. + } + + /// + /// Gets or sets a value containing the target directory to prefix values with. + /// + public string BaseDirectory + { + get { return _baseDirectory; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + _baseDirectory = Path.GetFullPath(value); + } + } + + /// + /// Allow parent directory traversal in file paths (e.g. ../file) + /// + public bool AllowParentTraversal + { + get => _allowParentTraversal; + set => _allowParentTraversal = value; + } + + /// + /// Gets or sets a value indicating whether paths on incoming values should be removed. + /// + public bool TrimIncomingPaths + { + get { return _trimIncomingPaths; } + set { _trimIncomingPaths = value; } + } + + /// + /// Transform a Zip directory name to a windows directory name. + /// + /// The directory name to transform. + /// The transformed name. + public string TransformDirectory(string name) + { + name = TransformFile(name); + if (name.Length > 0) + { + while (name.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) + { + name = name.Remove(name.Length - 1, 1); + } + } + else + { + throw new InvalidNameException("Cannot have an empty directory name"); + } + return name; + } + + /// + /// Transform a Zip format file name to a windows style one. + /// + /// The file name to transform. + /// The transformed name. + public string TransformFile(string name) + { + if (name != null) + { + name = MakeValidName(name, _replacementChar); + + if (_trimIncomingPaths) + { + name = Path.GetFileName(name); + } + + // This may exceed windows length restrictions. + // Combine will throw a PathTooLongException in that case. + if (_baseDirectory != null) + { + name = Path.Combine(_baseDirectory, name); + + // Ensure base directory ends with directory separator ('/' or '\' depending on OS) + var pathBase = Path.GetFullPath(_baseDirectory); + if (pathBase[pathBase.Length - 1] != Path.DirectorySeparatorChar) + { + pathBase += Path.DirectorySeparatorChar; + } + + if (!_allowParentTraversal && !Path.GetFullPath(name).StartsWith(pathBase, StringComparison.InvariantCultureIgnoreCase)) + { + throw new InvalidNameException("Parent traversal in paths is not allowed"); + } + } + } + else + { + name = string.Empty; + } + return name; + } + + /// + /// Test a name to see if it is a valid name for a windows filename as extracted from a Zip archive. + /// + /// The name to test. + /// Returns true if the name is a valid zip name; false otherwise. + /// The filename isnt a true windows path in some fundamental ways like no absolute paths, no rooted paths etc. + public static bool IsValidName(string name) + { + bool result = + (name != null) && + (name.Length <= MaxPath) && + (string.Compare(name, MakeValidName(name, '_'), StringComparison.Ordinal) == 0) + ; + + return result; + } + + /// + /// Force a name to be valid by replacing invalid characters with a fixed value + /// + /// The name to make valid + /// The replacement character to use for any invalid characters. + /// Returns a valid name + public static string MakeValidName(string name, char replacement) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + name = PathUtils.DropPathRoot(name.Replace("/", Path.DirectorySeparatorChar.ToString())); + + // Drop any leading slashes. + while ((name.Length > 0) && (name[0] == Path.DirectorySeparatorChar)) + { + name = name.Remove(0, 1); + } + + // Drop any trailing slashes. + while ((name.Length > 0) && (name[name.Length - 1] == Path.DirectorySeparatorChar)) + { + name = name.Remove(name.Length - 1, 1); + } + + // Convert consecutive \\ characters to \ + int index = name.IndexOf(string.Format("{0}{0}", Path.DirectorySeparatorChar), StringComparison.Ordinal); + while (index >= 0) + { + name = name.Remove(index, 1); + index = name.IndexOf(string.Format("{0}{0}", Path.DirectorySeparatorChar), StringComparison.Ordinal); + } + + // Convert any invalid characters using the replacement one. + index = name.IndexOfAny(InvalidEntryChars); + if (index >= 0) + { + var builder = new StringBuilder(name); + + while (index >= 0) + { + builder[index] = replacement; + + if (index >= name.Length) + { + index = -1; + } + else + { + index = name.IndexOfAny(InvalidEntryChars, index + 1); + } + } + name = builder.ToString(); + } + + // Check for names greater than MaxPath characters. + // TODO: Were is CLR version of MaxPath defined? Can't find it in Environment. + if (name.Length > MaxPath) + { + throw new PathTooLongException(); + } + + return name; + } + + /// + /// Gets or set the character to replace invalid characters during transformations. + /// + public char Replacement + { + get { return _replacementChar; } + set + { + for (int i = 0; i < InvalidEntryChars.Length; ++i) + { + if (InvalidEntryChars[i] == value) + { + throw new ArgumentException("invalid path character"); + } + } + + if ((value == Path.DirectorySeparatorChar) || (value == Path.AltDirectorySeparatorChar)) + { + throw new ArgumentException("invalid replacement character"); + } + + _replacementChar = value; + } + } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipConstants.cs b/ICSharpCode.SharpZipLib/Zip/ZipConstants.cs new file mode 100644 index 000000000000..6d4892d55035 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipConstants.cs @@ -0,0 +1,475 @@ +using System; + +namespace ICSharpCode.SharpZipLib.Zip +{ + #region Enumerations + + /// + /// Determines how entries are tested to see if they should use Zip64 extensions or not. + /// + public enum UseZip64 + { + /// + /// Zip64 will not be forced on entries during processing. + /// + /// An entry can have this overridden if required + Off, + + /// + /// Zip64 should always be used. + /// + On, + + /// + /// #ZipLib will determine use based on entry values when added to archive. + /// + Dynamic, + } + + /// + /// The kind of compression used for an entry in an archive + /// + public enum CompressionMethod + { + /// + /// A direct copy of the file contents is held in the archive + /// + Stored = 0, + + /// + /// Common Zip compression method using a sliding dictionary + /// of up to 32KB and secondary compression from Huffman/Shannon-Fano trees + /// + Deflated = 8, + + /// + /// An extension to deflate with a 64KB window. Not supported by #Zip currently + /// + Deflate64 = 9, + + /// + /// BZip2 compression. Not supported by #Zip. + /// + BZip2 = 12, + + /// + /// LZMA compression. Not supported by #Zip. + /// + LZMA = 14, + + /// + /// PPMd compression. Not supported by #Zip. + /// + PPMd = 98, + + /// + /// WinZip special for AES encryption, Now supported by #Zip. + /// + WinZipAES = 99, + } + + /// + /// Identifies the encryption algorithm used for an entry + /// + public enum EncryptionAlgorithm + { + /// + /// No encryption has been used. + /// + None = 0, + + /// + /// Encrypted using PKZIP 2.0 or 'classic' encryption. + /// + PkzipClassic = 1, + + /// + /// DES encryption has been used. + /// + Des = 0x6601, + + /// + /// RC2 encryption has been used for encryption. + /// + RC2 = 0x6602, + + /// + /// Triple DES encryption with 168 bit keys has been used for this entry. + /// + TripleDes168 = 0x6603, + + /// + /// Triple DES with 112 bit keys has been used for this entry. + /// + TripleDes112 = 0x6609, + + /// + /// AES 128 has been used for encryption. + /// + Aes128 = 0x660e, + + /// + /// AES 192 has been used for encryption. + /// + Aes192 = 0x660f, + + /// + /// AES 256 has been used for encryption. + /// + Aes256 = 0x6610, + + /// + /// RC2 corrected has been used for encryption. + /// + RC2Corrected = 0x6702, + + /// + /// Blowfish has been used for encryption. + /// + Blowfish = 0x6720, + + /// + /// Twofish has been used for encryption. + /// + Twofish = 0x6721, + + /// + /// RC4 has been used for encryption. + /// + RC4 = 0x6801, + + /// + /// An unknown algorithm has been used for encryption. + /// + Unknown = 0xffff + } + + /// + /// Defines the contents of the general bit flags field for an archive entry. + /// + [Flags] + public enum GeneralBitFlags + { + /// + /// Bit 0 if set indicates that the file is encrypted + /// + Encrypted = 0x0001, + + /// + /// Bits 1 and 2 - Two bits defining the compression method (only for Method 6 Imploding and 8,9 Deflating) + /// + Method = 0x0006, + + /// + /// Bit 3 if set indicates a trailing data descriptor is appended to the entry data + /// + Descriptor = 0x0008, + + /// + /// Bit 4 is reserved for use with method 8 for enhanced deflation + /// + ReservedPKware4 = 0x0010, + + /// + /// Bit 5 if set indicates the file contains Pkzip compressed patched data. + /// Requires version 2.7 or greater. + /// + Patched = 0x0020, + + /// + /// Bit 6 if set indicates strong encryption has been used for this entry. + /// + StrongEncryption = 0x0040, + + /// + /// Bit 7 is currently unused + /// + Unused7 = 0x0080, + + /// + /// Bit 8 is currently unused + /// + Unused8 = 0x0100, + + /// + /// Bit 9 is currently unused + /// + Unused9 = 0x0200, + + /// + /// Bit 10 is currently unused + /// + Unused10 = 0x0400, + + /// + /// Bit 11 if set indicates the filename and + /// comment fields for this file must be encoded using UTF-8. + /// + UnicodeText = 0x0800, + + /// + /// Bit 12 is documented as being reserved by PKware for enhanced compression. + /// + EnhancedCompress = 0x1000, + + /// + /// Bit 13 if set indicates that values in the local header are masked to hide + /// their actual values, and the central directory is encrypted. + /// + /// + /// Used when encrypting the central directory contents. + /// + HeaderMasked = 0x2000, + + /// + /// Bit 14 is documented as being reserved for use by PKware + /// + ReservedPkware14 = 0x4000, + + /// + /// Bit 15 is documented as being reserved for use by PKware + /// + ReservedPkware15 = 0x8000 + } + + #endregion Enumerations + + /// + /// This class contains constants used for Zip format files + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "kept for backwards compatibility")] + public static class ZipConstants + { + #region Versions + + /// + /// The version made by field for entries in the central header when created by this library + /// + /// + /// This is also the Zip version for the library when comparing against the version required to extract + /// for an entry. See . + /// + public const int VersionMadeBy = 51; // was 45 before AES + + /// + /// The version made by field for entries in the central header when created by this library + /// + /// + /// This is also the Zip version for the library when comparing against the version required to extract + /// for an entry. See ZipInputStream.CanDecompressEntry. + /// + [Obsolete("Use VersionMadeBy instead")] + public const int VERSION_MADE_BY = 51; + + /// + /// The minimum version required to support strong encryption + /// + public const int VersionStrongEncryption = 50; + + /// + /// The minimum version required to support strong encryption + /// + [Obsolete("Use VersionStrongEncryption instead")] + public const int VERSION_STRONG_ENCRYPTION = 50; + + /// + /// Version indicating AES encryption + /// + public const int VERSION_AES = 51; + + /// + /// The version required for Zip64 extensions (4.5 or higher) + /// + public const int VersionZip64 = 45; + + /// + /// The version required for BZip2 compression (4.6 or higher) + /// + public const int VersionBZip2 = 46; + + #endregion Versions + + #region Header Sizes + + /// + /// Size of local entry header (excluding variable length fields at end) + /// + public const int LocalHeaderBaseSize = 30; + + /// + /// Size of local entry header (excluding variable length fields at end) + /// + [Obsolete("Use LocalHeaderBaseSize instead")] + public const int LOCHDR = 30; + + /// + /// Size of Zip64 data descriptor + /// + public const int Zip64DataDescriptorSize = 24; + + /// + /// Size of data descriptor + /// + public const int DataDescriptorSize = 16; + + /// + /// Size of data descriptor + /// + [Obsolete("Use DataDescriptorSize instead")] + public const int EXTHDR = 16; + + /// + /// Size of central header entry (excluding variable fields) + /// + public const int CentralHeaderBaseSize = 46; + + /// + /// Size of central header entry + /// + [Obsolete("Use CentralHeaderBaseSize instead")] + public const int CENHDR = 46; + + /// + /// Size of end of central record (excluding variable fields) + /// + public const int EndOfCentralRecordBaseSize = 22; + + /// + /// Size of end of central record (excluding variable fields) + /// + [Obsolete("Use EndOfCentralRecordBaseSize instead")] + public const int ENDHDR = 22; + + /// + /// Size of 'classic' cryptographic header stored before any entry data + /// + public const int CryptoHeaderSize = 12; + + /// + /// Size of cryptographic header stored before entry data + /// + [Obsolete("Use CryptoHeaderSize instead")] + public const int CRYPTO_HEADER_SIZE = 12; + + /// + /// The size of the Zip64 central directory locator. + /// + public const int Zip64EndOfCentralDirectoryLocatorSize = 20; + + #endregion Header Sizes + + #region Header Signatures + + /// + /// Signature for local entry header + /// + public const int LocalHeaderSignature = 'P' | ('K' << 8) | (3 << 16) | (4 << 24); + + /// + /// Signature for local entry header + /// + [Obsolete("Use LocalHeaderSignature instead")] + public const int LOCSIG = 'P' | ('K' << 8) | (3 << 16) | (4 << 24); + + /// + /// Signature for spanning entry + /// + public const int SpanningSignature = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); + + /// + /// Signature for spanning entry + /// + [Obsolete("Use SpanningSignature instead")] + public const int SPANNINGSIG = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); + + /// + /// Signature for temporary spanning entry + /// + public const int SpanningTempSignature = 'P' | ('K' << 8) | ('0' << 16) | ('0' << 24); + + /// + /// Signature for temporary spanning entry + /// + [Obsolete("Use SpanningTempSignature instead")] + public const int SPANTEMPSIG = 'P' | ('K' << 8) | ('0' << 16) | ('0' << 24); + + /// + /// Signature for data descriptor + /// + /// + /// This is only used where the length, Crc, or compressed size isnt known when the + /// entry is created and the output stream doesnt support seeking. + /// The local entry cannot be 'patched' with the correct values in this case + /// so the values are recorded after the data prefixed by this header, as well as in the central directory. + /// + public const int DataDescriptorSignature = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); + + /// + /// Signature for data descriptor + /// + /// + /// This is only used where the length, Crc, or compressed size isnt known when the + /// entry is created and the output stream doesnt support seeking. + /// The local entry cannot be 'patched' with the correct values in this case + /// so the values are recorded after the data prefixed by this header, as well as in the central directory. + /// + [Obsolete("Use DataDescriptorSignature instead")] + public const int EXTSIG = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); + + /// + /// Signature for central header + /// + [Obsolete("Use CentralHeaderSignature instead")] + public const int CENSIG = 'P' | ('K' << 8) | (1 << 16) | (2 << 24); + + /// + /// Signature for central header + /// + public const int CentralHeaderSignature = 'P' | ('K' << 8) | (1 << 16) | (2 << 24); + + /// + /// Signature for Zip64 central file header + /// + public const int Zip64CentralFileHeaderSignature = 'P' | ('K' << 8) | (6 << 16) | (6 << 24); + + /// + /// Signature for Zip64 central file header + /// + [Obsolete("Use Zip64CentralFileHeaderSignature instead")] + public const int CENSIG64 = 'P' | ('K' << 8) | (6 << 16) | (6 << 24); + + /// + /// Signature for Zip64 central directory locator + /// + public const int Zip64CentralDirLocatorSignature = 'P' | ('K' << 8) | (6 << 16) | (7 << 24); + + /// + /// Signature for archive extra data signature (were headers are encrypted). + /// + public const int ArchiveExtraDataSignature = 'P' | ('K' << 8) | (6 << 16) | (7 << 24); + + /// + /// Central header digital signature + /// + public const int CentralHeaderDigitalSignature = 'P' | ('K' << 8) | (5 << 16) | (5 << 24); + + /// + /// Central header digital signature + /// + [Obsolete("Use CentralHeaderDigitalSignaure instead")] + public const int CENDIGITALSIG = 'P' | ('K' << 8) | (5 << 16) | (5 << 24); + + /// + /// End of central directory record signature + /// + public const int EndOfCentralDirectorySignature = 'P' | ('K' << 8) | (5 << 16) | (6 << 24); + + /// + /// End of central directory record signature + /// + [Obsolete("Use EndOfCentralDirectorySignature instead")] + public const int ENDSIG = 'P' | ('K' << 8) | (5 << 16) | (6 << 24); + + #endregion Header Signatures + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipEncryptionMethod.cs b/ICSharpCode.SharpZipLib/Zip/ZipEncryptionMethod.cs new file mode 100644 index 000000000000..ed51559cdf6b --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipEncryptionMethod.cs @@ -0,0 +1,28 @@ +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// The method of encrypting entries when creating zip archives. + /// + public enum ZipEncryptionMethod + { + /// + /// No encryption will be used. + /// + None, + + /// + /// Encrypt entries with ZipCrypto. + /// + ZipCrypto, + + /// + /// Encrypt entries with AES 128. + /// + AES128, + + /// + /// Encrypt entries with AES 256. + /// + AES256 + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs b/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs new file mode 100644 index 000000000000..b0bf15821bd8 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs @@ -0,0 +1,1157 @@ +using System; +using System.IO; +using System.Text; + +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// Defines known values for the property. + /// + public enum HostSystemID + { + /// + /// Host system = MSDOS + /// + Msdos = 0, + + /// + /// Host system = Amiga + /// + Amiga = 1, + + /// + /// Host system = Open VMS + /// + OpenVms = 2, + + /// + /// Host system = Unix + /// + Unix = 3, + + /// + /// Host system = VMCms + /// + VMCms = 4, + + /// + /// Host system = Atari ST + /// + AtariST = 5, + + /// + /// Host system = OS2 + /// + OS2 = 6, + + /// + /// Host system = Macintosh + /// + Macintosh = 7, + + /// + /// Host system = ZSystem + /// + ZSystem = 8, + + /// + /// Host system = Cpm + /// + Cpm = 9, + + /// + /// Host system = Windows NT + /// + WindowsNT = 10, + + /// + /// Host system = MVS + /// + MVS = 11, + + /// + /// Host system = VSE + /// + Vse = 12, + + /// + /// Host system = Acorn RISC + /// + AcornRisc = 13, + + /// + /// Host system = VFAT + /// + Vfat = 14, + + /// + /// Host system = Alternate MVS + /// + AlternateMvs = 15, + + /// + /// Host system = BEOS + /// + BeOS = 16, + + /// + /// Host system = Tandem + /// + Tandem = 17, + + /// + /// Host system = OS400 + /// + OS400 = 18, + + /// + /// Host system = OSX + /// + OSX = 19, + + /// + /// Host system = WinZIP AES + /// + WinZipAES = 99, + } + + /// + /// This class represents an entry in a zip archive. This can be a file + /// or a directory + /// ZipFile and ZipInputStream will give you instances of this class as + /// information about the members in an archive. ZipOutputStream + /// uses an instance of this class when creating an entry in a Zip file. + ///
+ ///
Author of the original java version : Jochen Hoenicke + ///
+ public class ZipEntry + { + [Flags] + private enum Known : byte + { + None = 0, + Size = 0x01, + CompressedSize = 0x02, + Crc = 0x04, + Time = 0x08, + ExternalAttributes = 0x10, + } + + #region Constructors + + /// + /// Creates a zip entry with the given name. + /// + /// + /// The name for this entry. Can include directory components. + /// The convention for names is 'unix' style paths with relative names only. + /// There are with no device names and path elements are separated by '/' characters. + /// + /// + /// The name passed is null + /// + public ZipEntry(string name) + : this(name, 0, ZipConstants.VersionMadeBy, CompressionMethod.Deflated, true) + { + } + + /// + /// Creates a zip entry with the given name and version required to extract + /// + /// + /// The name for this entry. Can include directory components. + /// The convention for names is 'unix' style paths with no device names and + /// path elements separated by '/' characters. This is not enforced see CleanName + /// on how to ensure names are valid if this is desired. + /// + /// + /// The minimum 'feature version' required this entry + /// + /// + /// The name passed is null + /// + internal ZipEntry(string name, int versionRequiredToExtract) + : this(name, versionRequiredToExtract, ZipConstants.VersionMadeBy, + CompressionMethod.Deflated, true) + { + } + + /// + /// Initializes an entry with the given name and made by information + /// + /// Name for this entry + /// Version and HostSystem Information + /// Minimum required zip feature version required to extract this entry + /// Compression method for this entry. + /// Whether the entry uses unicode for name and comment + /// + /// The name passed is null + /// + /// + /// versionRequiredToExtract should be 0 (auto-calculate) or > 10 + /// + /// + /// This constructor is used by the ZipFile class when reading from the central header + /// It is not generally useful, use the constructor specifying the name only. + /// + internal ZipEntry(string name, int versionRequiredToExtract, int madeByInfo, + CompressionMethod method, bool unicode) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (name.Length > 0xffff) + { + throw new ArgumentException("Name is too long", nameof(name)); + } + + if ((versionRequiredToExtract != 0) && (versionRequiredToExtract < 10)) + { + throw new ArgumentOutOfRangeException(nameof(versionRequiredToExtract)); + } + + this.DateTime = DateTime.Now; + this.name = name; + this.versionMadeBy = (ushort)madeByInfo; + this.versionToExtract = (ushort)versionRequiredToExtract; + this.method = method; + + IsUnicodeText = unicode; + } + + /// + /// Creates a deep copy of the given zip entry. + /// + /// + /// The entry to copy. + /// + [Obsolete("Use Clone instead")] + public ZipEntry(ZipEntry entry) + { + if (entry == null) + { + throw new ArgumentNullException(nameof(entry)); + } + + known = entry.known; + name = entry.name; + size = entry.size; + compressedSize = entry.compressedSize; + crc = entry.crc; + dateTime = entry.DateTime; + method = entry.method; + comment = entry.comment; + versionToExtract = entry.versionToExtract; + versionMadeBy = entry.versionMadeBy; + externalFileAttributes = entry.externalFileAttributes; + flags = entry.flags; + + zipFileIndex = entry.zipFileIndex; + offset = entry.offset; + + forceZip64_ = entry.forceZip64_; + + if (entry.extra != null) + { + extra = new byte[entry.extra.Length]; + Array.Copy(entry.extra, 0, extra, 0, entry.extra.Length); + } + } + + #endregion Constructors + + /// + /// Get a value indicating whether the entry has a CRC value available. + /// + public bool HasCrc => (known & Known.Crc) != 0; + + /// + /// Get/Set flag indicating if entry is encrypted. + /// A simple helper routine to aid interpretation of flags + /// + /// This is an assistant that interprets the flags property. + public bool IsCrypted + { + get => this.HasFlag(GeneralBitFlags.Encrypted); + set => this.SetFlag(GeneralBitFlags.Encrypted, value); + } + + /// + /// Get / set a flag indicating whether entry name and comment text are + /// encoded in unicode UTF8. + /// + /// This is an assistant that interprets the flags property. + public bool IsUnicodeText + { + get => this.HasFlag(GeneralBitFlags.UnicodeText); + set => this.SetFlag(GeneralBitFlags.UnicodeText, value); + } + + /// + /// Value used during password checking for PKZIP 2.0 / 'classic' encryption. + /// + internal byte CryptoCheckValue + { + get => cryptoCheckValue_; + set => cryptoCheckValue_ = value; + } + + /// + /// Get/Set general purpose bit flag for entry + /// + /// + /// General purpose bit flag
+ ///
+ /// Bit 0: If set, indicates the file is encrypted
+ /// Bit 1-2 Only used for compression type 6 Imploding, and 8, 9 deflating
+ /// Imploding:
+ /// Bit 1 if set indicates an 8K sliding dictionary was used. If clear a 4k dictionary was used
+ /// Bit 2 if set indicates 3 Shannon-Fanno trees were used to encode the sliding dictionary, 2 otherwise
+ ///
+ /// Deflating:
+ /// Bit 2 Bit 1
+ /// 0 0 Normal compression was used
+ /// 0 1 Maximum compression was used
+ /// 1 0 Fast compression was used
+ /// 1 1 Super fast compression was used
+ ///
+ /// Bit 3: If set, the fields crc-32, compressed size + /// and uncompressed size are were not able to be written during zip file creation + /// The correct values are held in a data descriptor immediately following the compressed data.
+ /// Bit 4: Reserved for use by PKZIP for enhanced deflating
+ /// Bit 5: If set indicates the file contains compressed patch data
+ /// Bit 6: If set indicates strong encryption was used.
+ /// Bit 7-10: Unused or reserved
+ /// Bit 11: If set the name and comments for this entry are in unicode.
+ /// Bit 12-15: Unused or reserved
+ ///
+ /// + /// + public int Flags + { + get => flags; + set => flags = value; + } + + /// + /// Get/Set index of this entry in Zip file + /// + /// This is only valid when the entry is part of a + public long ZipFileIndex + { + get => zipFileIndex; + set => zipFileIndex = value; + } + + /// + /// Get/set offset for use in central header + /// + public long Offset + { + get => offset; + set => offset = value; + } + + /// + /// Get/Set external file attributes as an integer. + /// The values of this are operating system dependent see + /// HostSystem for details + /// + public int ExternalFileAttributes + { + get => (known & Known.ExternalAttributes) == 0 ? -1 : externalFileAttributes; + + set + { + externalFileAttributes = value; + known |= Known.ExternalAttributes; + } + } + + /// + /// Get the version made by for this entry or zero if unknown. + /// The value / 10 indicates the major version number, and + /// the value mod 10 is the minor version number + /// + public int VersionMadeBy => versionMadeBy & 0xff; + + /// + /// Get a value indicating this entry is for a DOS/Windows system. + /// + public bool IsDOSEntry + => (HostSystem == (int)HostSystemID.Msdos) + || (HostSystem == (int)HostSystemID.WindowsNT); + + /// + /// Test the external attributes for this to + /// see if the external attributes are Dos based (including WINNT and variants) + /// and match the values + /// + /// The attributes to test. + /// Returns true if the external attributes are known to be DOS/Windows + /// based and have the same attributes set as the value passed. + private bool HasDosAttributes(int attributes) + { + bool result = false; + if ((known & Known.ExternalAttributes) != 0) + { + result |= (((HostSystem == (int)HostSystemID.Msdos) || + (HostSystem == (int)HostSystemID.WindowsNT)) && + (ExternalFileAttributes & attributes) == attributes); + } + return result; + } + + /// + /// Gets the compatibility information for the external file attribute + /// 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. Otherwise the value + /// will be non-zero and identify the host system on which the attributes are compatible. + /// + /// + /// + /// The values for this as defined in the Zip File format and by others are shown below. The values are somewhat + /// misleading in some cases as they are not all used as shown. You should consult the relevant documentation + /// to obtain up to date and correct information. The modified appnote by the infozip group is + /// particularly helpful as it documents a lot of peculiarities. The document is however a little dated. + /// + /// 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 HPFS + /// 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) + /// 99 - WinZip AES + /// remainder - unused + /// + /// + public int HostSystem + { + get => (versionMadeBy >> 8) & 0xff; + + set + { + versionMadeBy &= 0x00ff; + versionMadeBy |= (ushort)((value & 0xff) << 8); + } + } + + /// + /// Get minimum Zip feature version required to extract this entry + /// + /// + /// Minimum features are defined as:
+ /// 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 encryption
+ /// 2.1 - File is compressed using Deflate64
+ /// 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.1 - File is encrypted using corrected RC2-64 encryption
+ /// 6.1 - File is encrypted using non-OAEP key wrapping
+ /// 6.2 - Central directory encryption (not confirmed yet)
+ /// 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
+ ///
+ /// + public int Version + { + get + { + // Return recorded version if known. + if (versionToExtract != 0) + // Only lower order byte. High order is O/S file system. + return versionToExtract & 0x00ff; + + if (AESKeySize > 0) + // Ver 5.1 = AES + return ZipConstants.VERSION_AES; + + if (CompressionMethod.BZip2 == method) + return ZipConstants.VersionBZip2; + + if (CentralHeaderRequiresZip64) + return ZipConstants.VersionZip64; + + if (CompressionMethod.Deflated == method || IsDirectory || IsCrypted) + return 20; + + if (HasDosAttributes(0x08)) + return 11; + + return 10; + } + } + + /// + /// Get a value indicating whether this entry can be decompressed by the library. + /// + /// This is based on the and + /// whether the compression method is supported. + public bool CanDecompress + => Version <= ZipConstants.VersionMadeBy + && (Version == 10 || Version == 11 || Version == 20 || Version == 45 || Version == 46 || Version == 51) + && IsCompressionMethodSupported(); + + /// + /// Force this entry to be recorded using Zip64 extensions. + /// + public void ForceZip64() => forceZip64_ = true; + + /// + /// Get a value indicating whether Zip64 extensions were forced. + /// + /// A value of true if Zip64 extensions have been forced on; false if not. + public bool IsZip64Forced() => forceZip64_; + + /// + /// Gets a value indicating if the entry requires Zip64 extensions + /// to store the full entry values. + /// + /// A value of true if a local header requires Zip64 extensions; false if not. + public bool LocalHeaderRequiresZip64 + { + get + { + bool result = forceZip64_; + + if (!result) + { + ulong trueCompressedSize = compressedSize; + + if ((versionToExtract == 0) && IsCrypted) + { + trueCompressedSize += (ulong)this.EncryptionOverheadSize; + } + + // TODO: A better estimation of the true limit based on compression overhead should be used + // to determine when an entry should use Zip64. + result = + ((this.size >= uint.MaxValue) || (trueCompressedSize >= uint.MaxValue)) && + ((versionToExtract == 0) || (versionToExtract >= ZipConstants.VersionZip64)); + } + + return result; + } + } + + /// + /// Get a value indicating whether the central directory entry requires Zip64 extensions to be stored. + /// + public bool CentralHeaderRequiresZip64 + => LocalHeaderRequiresZip64 || (offset >= uint.MaxValue); + + /// + /// Get/Set DosTime value. + /// + /// + /// The MS-DOS date format can only represent dates between 1/1/1980 and 12/31/2107. + /// + public long DosTime + { + get + { + if ((known & Known.Time) == 0) + { + return 0; + } + + var year = (uint)DateTime.Year; + var month = (uint)DateTime.Month; + var day = (uint)DateTime.Day; + var hour = (uint)DateTime.Hour; + var minute = (uint)DateTime.Minute; + var second = (uint)DateTime.Second; + + if (year < 1980) + { + year = 1980; + month = 1; + day = 1; + hour = 0; + minute = 0; + second = 0; + } + else if (year > 2107) + { + year = 2107; + month = 12; + day = 31; + hour = 23; + minute = 59; + second = 59; + } + + return ((year - 1980) & 0x7f) << 25 | + (month << 21) | + (day << 16) | + (hour << 11) | + (minute << 5) | + (second >> 1); + } + + set + { + unchecked + { + var dosTime = (uint)value; + uint sec = Math.Min(59, 2 * (dosTime & 0x1f)); + uint min = Math.Min(59, (dosTime >> 5) & 0x3f); + uint hrs = Math.Min(23, (dosTime >> 11) & 0x1f); + uint mon = Math.Max(1, Math.Min(12, ((uint)(value >> 21) & 0xf))); + uint year = ((dosTime >> 25) & 0x7f) + 1980; + int day = Math.Max(1, Math.Min(DateTime.DaysInMonth((int)year, (int)mon), (int)((value >> 16) & 0x1f))); + DateTime = new DateTime((int)year, (int)mon, day, (int)hrs, (int)min, (int)sec, DateTimeKind.Unspecified); + } + } + } + + /// + /// Gets/Sets the time of last modification of the entry. + /// + /// + /// The property is updated to match this as far as possible. + /// + public DateTime DateTime + { + get => dateTime; + + set + { + dateTime = value; + known |= Known.Time; + } + } + + /// + /// Returns the entry name. + /// + /// + /// The unix naming convention is followed. + /// Path components in the entry should always separated by forward slashes ('/'). + /// Dos device names like C: should also be removed. + /// See the class, or + /// + public string Name + { + get => name; + internal set => name = value; + } + + /// + /// Gets/Sets the size of the uncompressed data. + /// + /// + /// The size or -1 if unknown. + /// + /// Setting the size before adding an entry to an archive can help + /// avoid compatibility problems with some archivers which don't understand Zip64 extensions. + public long Size + { + get => (known & Known.Size) != 0 ? (long)size : -1L; + set + { + size = (ulong)value; + known |= Known.Size; + } + } + + /// + /// Gets/Sets the size of the compressed data. + /// + /// + /// The compressed entry size or -1 if unknown. + /// + public long CompressedSize + { + get => (known & Known.CompressedSize) != 0 ? (long)compressedSize : -1L; + set + { + compressedSize = (ulong)value; + known |= Known.CompressedSize; + } + } + + /// + /// Gets/Sets the crc of the uncompressed data. + /// + /// + /// Crc is not in the range 0..0xffffffffL + /// + /// + /// The crc value or -1 if unknown. + /// + public long Crc + { + get => (known & Known.Crc) != 0 ? crc & 0xffffffffL : -1L; + set + { + if ((crc & 0xffffffff00000000L) != 0) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + this.crc = (uint)value; + this.known |= Known.Crc; + } + } + + /// + /// Gets/Sets the compression method. + /// + /// + /// The compression method for this entry + /// + public CompressionMethod CompressionMethod + { + get => method; + set => method = value; + } + + /// + /// Gets the compression method for outputting to the local or central header. + /// Returns same value as CompressionMethod except when AES encrypting, which + /// places 99 in the method and places the real method in the extra data. + /// + internal CompressionMethod CompressionMethodForHeader + => (AESKeySize > 0) ? CompressionMethod.WinZipAES : method; + + /// + /// Gets/Sets the extra data. + /// + /// + /// Extra data is longer than 64KB (0xffff) bytes. + /// + /// + /// Extra data or null if not set. + /// + public byte[] ExtraData + { + // TODO: This is slightly safer but less efficient. Think about whether it should change. + // return (byte[]) extra.Clone(); + get => extra; + + set + { + if (value == null) + { + extra = null; + } + else + { + if (value.Length > 0xffff) + { + throw new System.ArgumentOutOfRangeException(nameof(value)); + } + + extra = new byte[value.Length]; + Array.Copy(value, 0, extra, 0, value.Length); + } + } + } + + /// + /// For AES encrypted files returns or sets the number of bits of encryption (128, 192 or 256). + /// When setting, only 0 (off), 128 or 256 is supported. + /// + public int AESKeySize + { + get + { + // the strength (1 or 3) is in the entry header + switch (_aesEncryptionStrength) + { + case 0: + return 0; // Not AES + case 1: + return 128; + + case 2: + return 192; // Not used by WinZip + case 3: + return 256; + + default: + throw new ZipException("Invalid AESEncryptionStrength " + _aesEncryptionStrength); + } + } + set + { + switch (value) + { + case 0: + _aesEncryptionStrength = 0; + break; + + case 128: + _aesEncryptionStrength = 1; + break; + + case 256: + _aesEncryptionStrength = 3; + break; + + default: + throw new ZipException("AESKeySize must be 0, 128 or 256: " + value); + } + } + } + + /// + /// AES Encryption strength for storage in extra data in entry header. + /// 1 is 128 bit, 2 is 192 bit, 3 is 256 bit. + /// + internal byte AESEncryptionStrength => (byte)_aesEncryptionStrength; + + /// + /// Returns the length of the salt, in bytes + /// + /// Key size -> Salt length: 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes. + internal int AESSaltLen => AESKeySize / 16; + + /// + /// Number of extra bytes required to hold the AES Header fields (Salt, Pwd verify, AuthCode) + /// + /// File format: + /// Bytes | Content + /// ---------+--------------------------- + /// Variable | Salt value + /// 2 | Password verification value + /// Variable | Encrypted file data + /// 10 | Authentication code + internal int AESOverheadSize => 12 + AESSaltLen; + + /// + /// Number of extra bytes required to hold the encryption header fields. + /// + internal int EncryptionOverheadSize => + !IsCrypted + // Entry is not encrypted - no overhead + ? 0 + : _aesEncryptionStrength == 0 + // Entry is encrypted using ZipCrypto + ? ZipConstants.CryptoHeaderSize + // Entry is encrypted using AES + : AESOverheadSize; + + /// + /// Process extra data fields updating the entry based on the contents. + /// + /// True if the extra data fields should be handled + /// for a local header, rather than for a central header. + /// + internal void ProcessExtraData(bool localHeader) + { + var extraData = new ZipExtraData(this.extra); + + if (extraData.Find(0x0001)) + { + // Version required to extract is ignored here as some archivers dont set it correctly + // in theory it should be version 45 or higher + + // The recorded size will change but remember that this is zip64. + forceZip64_ = true; + + if (extraData.ValueLength < 4) + { + throw new ZipException("Extra data extended Zip64 information length is invalid"); + } + + // (localHeader ||) was deleted, because actually there is no specific difference with reading sizes between local header & central directory + // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + // ... + // 4.4 Explanation of fields + // ... + // 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. + // + // Otherwise there is problem with minizip implementation + if (size == uint.MaxValue) + { + size = (ulong)extraData.ReadLong(); + } + + if (compressedSize == uint.MaxValue) + { + compressedSize = (ulong)extraData.ReadLong(); + } + + if (!localHeader && (offset == uint.MaxValue)) + { + offset = extraData.ReadLong(); + } + + // Disk number on which file starts is ignored + } + else + { + if ( + ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) && + ((size == uint.MaxValue) || (compressedSize == uint.MaxValue)) + ) + { + throw new ZipException("Zip64 Extended information required but is missing."); + } + } + + DateTime = GetDateTime(extraData) ?? DateTime; + if (method == CompressionMethod.WinZipAES) + { + ProcessAESExtraData(extraData); + } + } + + private static DateTime? GetDateTime(ZipExtraData extraData) + { + // Check for NT timestamp + // NOTE: Disable by default to match behavior of InfoZIP +#if RESPECT_NT_TIMESTAMP + NTTaggedData ntData = extraData.GetData(); + if (ntData != null) + return ntData.LastModificationTime; +#endif + + // Check for Unix timestamp + ExtendedUnixData unixData = extraData.GetData(); + if (unixData != null && unixData.Include.HasFlag(ExtendedUnixData.Flags.ModificationTime)) + return unixData.ModificationTime; + + return null; + } + + // For AES the method in the entry is 99, and the real compression method is in the extradata + private void ProcessAESExtraData(ZipExtraData extraData) + { + if (extraData.Find(0x9901)) + { + // Set version for Zipfile.CreateAndInitDecryptionStream + versionToExtract = ZipConstants.VERSION_AES; // Ver 5.1 = AES see "Version" getter + + // + // Unpack AES extra data field see http://www.winzip.com/aes_info.htm + int length = extraData.ValueLength; // Data size currently 7 + if (length < 7) + throw new ZipException("AES Extra Data Length " + length + " invalid."); + int ver = extraData.ReadShort(); // Version number (1=AE-1 2=AE-2) + int vendorId = extraData.ReadShort(); // 2-character vendor ID 0x4541 = "AE" + int encrStrength = extraData.ReadByte(); // encryption strength 1 = 128 2 = 192 3 = 256 + int actualCompress = extraData.ReadShort(); // The actual compression method used to compress the file + _aesVer = ver; + _aesEncryptionStrength = encrStrength; + method = (CompressionMethod)actualCompress; + } + else + throw new ZipException("AES Extra Data missing"); + } + + /// + /// Gets/Sets the entry comment. + /// + /// + /// If comment is longer than 0xffff. + /// + /// + /// The comment or null if not set. + /// + /// + /// A comment is only available for entries when read via the class. + /// The class doesn't have the comment data available. + /// + public string Comment + { + get => comment; + set + { + // This test is strictly incorrect as the length is in characters + // while the storage limit is in bytes. + // While the test is partially correct in that a comment of this length or greater + // is definitely invalid, shorter comments may also have an invalid length + // where there are multi-byte characters + // The full test is not possible here however as the code page to apply conversions with + // isn't available. + if ((value != null) && (value.Length > 0xffff)) + { + throw new ArgumentOutOfRangeException(nameof(value), "cannot exceed 65535"); + } + + comment = value; + } + } + + /// + /// Gets a value indicating if the entry is a directory. + /// however. + /// + /// + /// A directory is determined by an entry name with a trailing slash '/'. + /// The external file attributes can also indicate an entry is for a directory. + /// Currently only dos/windows attributes are tested in this manner. + /// The trailing slash convention should always be followed. + /// + public bool IsDirectory + => name.Length > 0 + && (name[name.Length - 1] == '/' || name[name.Length - 1] == '\\') || HasDosAttributes(16); + + /// + /// Get a value of true if the entry appears to be a file; false otherwise + /// + /// + /// This only takes account of DOS/Windows attributes. Other operating systems are ignored. + /// For linux and others the result may be incorrect. + /// + public bool IsFile => !IsDirectory && !HasDosAttributes(8); + + /// + /// Test entry to see if data can be extracted. + /// + /// Returns true if data can be extracted for this entry; false otherwise. + public bool IsCompressionMethodSupported() => IsCompressionMethodSupported(CompressionMethod); + + #region ICloneable Members + + /// + /// Creates a copy of this zip entry. + /// + /// An that is a copy of the current instance. + public object Clone() + { + var result = (ZipEntry)this.MemberwiseClone(); + + // Ensure extra data is unique if it exists. + if (extra != null) + { + result.extra = new byte[extra.Length]; + Array.Copy(extra, 0, result.extra, 0, extra.Length); + } + + return result; + } + + #endregion ICloneable Members + + /// + /// Gets a string representation of this ZipEntry. + /// + /// A readable textual representation of this + public override string ToString() => name; + + /// + /// Test a compression method to see if this library + /// supports extracting data compressed with that method + /// + /// The compression method to test. + /// Returns true if the compression method is supported; false otherwise + public static bool IsCompressionMethodSupported(CompressionMethod method) + => method == CompressionMethod.Deflated + || method == CompressionMethod.Stored + || method == CompressionMethod.BZip2; + + /// + /// Cleans a name making it conform to Zip file conventions. + /// Devices names ('c:\') and UNC share names ('\\server\share') are removed + /// and back slashes ('\') are converted to forward slashes ('/'). + /// Names are made relative by trimming leading slashes which is compatible + /// with the ZIP naming convention. + /// + /// The name to clean + /// The 'cleaned' name. + /// + /// The Zip name transform class is more flexible. + /// + public static string CleanName(string name) + { + if (name == null) + { + return string.Empty; + } + + if (Path.IsPathRooted(name)) + { + // NOTE: + // for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt + name = name.Substring(Path.GetPathRoot(name).Length); + } + + name = name.Replace(@"\", "/"); + + while ((name.Length > 0) && (name[0] == '/')) + { + name = name.Remove(0, 1); + } + return name; + } + + #region Instance Fields + + private Known known; + private int externalFileAttributes = -1; // contains external attributes (O/S dependant) + + private ushort versionMadeBy; // Contains host system and version information + // only relevant for central header entries + + private string name; + private ulong size; + private ulong compressedSize; + private ushort versionToExtract; // Version required to extract (library handles <= 2.0) + private uint crc; + private DateTime dateTime; + + private CompressionMethod method = CompressionMethod.Deflated; + private byte[] extra; + private string comment; + + private int flags; // general purpose bit flags + + private long zipFileIndex = -1; // used by ZipFile + private long offset; // used by ZipFile and ZipOutputStream + + private bool forceZip64_; + private byte cryptoCheckValue_; + private int _aesVer; // Version number (2 = AE-2 ?). Assigned but not used. + private int _aesEncryptionStrength; // Encryption strength 1 = 128 2 = 192 3 = 256 + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipEntryExtensions.cs b/ICSharpCode.SharpZipLib/Zip/ZipEntryExtensions.cs new file mode 100644 index 000000000000..927e94cfe539 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipEntryExtensions.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// General ZipEntry helper extensions + /// + public static class ZipEntryExtensions + { + /// + /// Efficiently check if a flag is set without enum un-/boxing + /// + /// + /// + /// Returns whether the flag was set + public static bool HasFlag(this ZipEntry entry, GeneralBitFlags flag) + => (entry.Flags & (int) flag) != 0; + + /// + /// Efficiently set a flag without enum un-/boxing + /// + /// + /// + /// Whether the passed flag should be set (1) or cleared (0) + public static void SetFlag(this ZipEntry entry, GeneralBitFlags flag, bool enabled = true) + => entry.Flags = enabled + ? entry.Flags | (int) flag + : entry.Flags & ~(int) flag; + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs b/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs new file mode 100644 index 000000000000..ccbb26968654 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs @@ -0,0 +1,375 @@ +using ICSharpCode.SharpZipLib.Core; +using System; +using System.IO; + +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// Basic implementation of + /// + public class ZipEntryFactory : IEntryFactory + { + #region Enumerations + + /// + /// Defines the possible values to be used for the . + /// + public enum TimeSetting + { + /// + /// Use the recorded LastWriteTime value for the file. + /// + LastWriteTime, + + /// + /// Use the recorded LastWriteTimeUtc value for the file + /// + LastWriteTimeUtc, + + /// + /// Use the recorded CreateTime value for the file. + /// + CreateTime, + + /// + /// Use the recorded CreateTimeUtc value for the file. + /// + CreateTimeUtc, + + /// + /// Use the recorded LastAccessTime value for the file. + /// + LastAccessTime, + + /// + /// Use the recorded LastAccessTimeUtc value for the file. + /// + LastAccessTimeUtc, + + /// + /// Use a fixed value. + /// + /// The actual value used can be + /// specified via the constructor or + /// using the with the setting set + /// to which will use the when this class was constructed. + /// The property can also be used to set this value. + Fixed, + } + + #endregion Enumerations + + #region Constructors + + /// + /// Initialise a new instance of the class. + /// + /// A default , and the LastWriteTime for files is used. + public ZipEntryFactory() + { + nameTransform_ = new ZipNameTransform(); + isUnicodeText_ = true; + } + + /// + /// Initialise a new instance of using the specified + /// + /// The time setting to use when creating Zip entries. + public ZipEntryFactory(TimeSetting timeSetting) : this() + { + timeSetting_ = timeSetting; + } + + /// + /// Initialise a new instance of using the specified + /// + /// The time to set all values to. + public ZipEntryFactory(DateTime time) : this() + { + timeSetting_ = TimeSetting.Fixed; + FixedDateTime = time; + } + + #endregion Constructors + + #region Properties + + /// + /// Get / set the to be used when creating new values. + /// + /// + /// Setting this property to null will cause a default name transform to be used. + /// + public INameTransform NameTransform + { + get { return nameTransform_; } + set + { + if (value == null) + { + nameTransform_ = new ZipNameTransform(); + } + else + { + nameTransform_ = value; + } + } + } + + /// + /// Get / set the in use. + /// + public TimeSetting Setting + { + get { return timeSetting_; } + set { timeSetting_ = value; } + } + + /// + /// Get / set the value to use when is set to + /// + public DateTime FixedDateTime + { + get { return fixedDateTime_; } + set + { + if (value.Year < 1970) + { + throw new ArgumentException("Value is too old to be valid", nameof(value)); + } + fixedDateTime_ = value; + } + } + + /// + /// A bitmask defining the attributes to be retrieved from the actual file. + /// + /// The default is to get all possible attributes from the actual file. + public int GetAttributes + { + get { return getAttributes_; } + set { getAttributes_ = value; } + } + + /// + /// A bitmask defining which attributes are to be set on. + /// + /// By default no attributes are set on. + public int SetAttributes + { + get { return setAttributes_; } + set { setAttributes_ = value; } + } + + /// + /// Get set a value indicating whether unicode text should be set on. + /// + public bool IsUnicodeText + { + get { return isUnicodeText_; } + set { isUnicodeText_ = value; } + } + + #endregion Properties + + #region IEntryFactory Members + + /// + /// Make a new for a file. + /// + /// The name of the file to create a new entry for. + /// Returns a new based on the . + public ZipEntry MakeFileEntry(string fileName) + { + return MakeFileEntry(fileName, null, true); + } + + /// + /// Make a new for a file. + /// + /// The name of the file to create a new entry for. + /// If true entry detail is retrieved from the file system if the file exists. + /// Returns a new based on the . + public ZipEntry MakeFileEntry(string fileName, bool useFileSystem) + { + return MakeFileEntry(fileName, null, useFileSystem); + } + + /// + /// Make a new from a name. + /// + /// The name of the file to create a new entry for. + /// An alternative name to be used for the new entry. Null if not applicable. + /// If true entry detail is retrieved from the file system if the file exists. + /// Returns a new based on the . + public ZipEntry MakeFileEntry(string fileName, string entryName, bool useFileSystem) + { + var result = new ZipEntry(nameTransform_.TransformFile(!string.IsNullOrEmpty(entryName) ? entryName : fileName)); + result.IsUnicodeText = isUnicodeText_; + + int externalAttributes = 0; + bool useAttributes = (setAttributes_ != 0); + + FileInfo fi = null; + if (useFileSystem) + { + fi = new FileInfo(fileName); + } + + if ((fi != null) && fi.Exists) + { + switch (timeSetting_) + { + case TimeSetting.CreateTime: + result.DateTime = fi.CreationTime; + break; + + case TimeSetting.CreateTimeUtc: + result.DateTime = fi.CreationTimeUtc; + break; + + case TimeSetting.LastAccessTime: + result.DateTime = fi.LastAccessTime; + break; + + case TimeSetting.LastAccessTimeUtc: + result.DateTime = fi.LastAccessTimeUtc; + break; + + case TimeSetting.LastWriteTime: + result.DateTime = fi.LastWriteTime; + break; + + case TimeSetting.LastWriteTimeUtc: + result.DateTime = fi.LastWriteTimeUtc; + break; + + case TimeSetting.Fixed: + result.DateTime = fixedDateTime_; + break; + + default: + throw new ZipException("Unhandled time setting in MakeFileEntry"); + } + + result.Size = fi.Length; + + useAttributes = true; + externalAttributes = ((int)fi.Attributes & getAttributes_); + } + else + { + if (timeSetting_ == TimeSetting.Fixed) + { + result.DateTime = fixedDateTime_; + } + } + + if (useAttributes) + { + externalAttributes |= setAttributes_; + result.ExternalFileAttributes = externalAttributes; + } + + return result; + } + + /// + /// Make a new for a directory. + /// + /// The raw untransformed name for the new directory + /// Returns a new representing a directory. + public ZipEntry MakeDirectoryEntry(string directoryName) + { + return MakeDirectoryEntry(directoryName, true); + } + + /// + /// Make a new for a directory. + /// + /// The raw untransformed name for the new directory + /// If true entry detail is retrieved from the file system if the file exists. + /// Returns a new representing a directory. + public ZipEntry MakeDirectoryEntry(string directoryName, bool useFileSystem) + { + var result = new ZipEntry(nameTransform_.TransformDirectory(directoryName)); + result.IsUnicodeText = isUnicodeText_; + result.Size = 0; + + int externalAttributes = 0; + + DirectoryInfo di = null; + + if (useFileSystem) + { + di = new DirectoryInfo(directoryName); + } + + if ((di != null) && di.Exists) + { + switch (timeSetting_) + { + case TimeSetting.CreateTime: + result.DateTime = di.CreationTime; + break; + + case TimeSetting.CreateTimeUtc: + result.DateTime = di.CreationTimeUtc; + break; + + case TimeSetting.LastAccessTime: + result.DateTime = di.LastAccessTime; + break; + + case TimeSetting.LastAccessTimeUtc: + result.DateTime = di.LastAccessTimeUtc; + break; + + case TimeSetting.LastWriteTime: + result.DateTime = di.LastWriteTime; + break; + + case TimeSetting.LastWriteTimeUtc: + result.DateTime = di.LastWriteTimeUtc; + break; + + case TimeSetting.Fixed: + result.DateTime = fixedDateTime_; + break; + + default: + throw new ZipException("Unhandled time setting in MakeDirectoryEntry"); + } + + externalAttributes = ((int)di.Attributes & getAttributes_); + } + else + { + if (timeSetting_ == TimeSetting.Fixed) + { + result.DateTime = fixedDateTime_; + } + } + + // Always set directory attribute on. + externalAttributes |= (setAttributes_ | 16); + result.ExternalFileAttributes = externalAttributes; + + return result; + } + + #endregion IEntryFactory Members + + #region Instance Fields + + private INameTransform nameTransform_; + private DateTime fixedDateTime_ = DateTime.Now; + private TimeSetting timeSetting_ = TimeSetting.LastWriteTime; + private bool isUnicodeText_; + + private int getAttributes_ = -1; + private int setAttributes_; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipException.cs b/ICSharpCode.SharpZipLib/Zip/ZipException.cs new file mode 100644 index 000000000000..ef8142b6543c --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipException.cs @@ -0,0 +1,54 @@ +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// ZipException represents exceptions specific to Zip classes and code. + /// + [Serializable] + public class ZipException : SharpZipBaseException + { + /// + /// Initialise a new instance of . + /// + public ZipException() + { + } + + /// + /// Initialise a new instance of with its message string. + /// + /// A that describes the error. + public ZipException(string message) + : base(message) + { + } + + /// + /// Initialise a new instance of . + /// + /// A that describes the error. + /// The that caused this exception. + public ZipException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the ZipException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected ZipException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs b/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs new file mode 100644 index 000000000000..cc2e74490fe0 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs @@ -0,0 +1,974 @@ +using System; +using System.IO; +using ICSharpCode.SharpZipLib.Core; + +namespace ICSharpCode.SharpZipLib.Zip +{ + // TODO: Sort out whether tagged data is useful and what a good implementation might look like. + // Its just a sketch of an idea at the moment. + + /// + /// ExtraData tagged value interface. + /// + public interface ITaggedData + { + /// + /// Get the ID for this tagged data value. + /// + ushort TagID { get; } + + /// + /// Set the contents of this instance from the data passed. + /// + /// The data to extract contents from. + /// The offset to begin extracting data from. + /// The number of bytes to extract. + void SetData(byte[] data, int offset, int count); + + /// + /// Get the data representing this instance. + /// + /// Returns the data for this instance. + byte[] GetData(); + } + + /// + /// A raw binary tagged value + /// + public class RawTaggedData : ITaggedData + { + /// + /// Initialise a new instance. + /// + /// The tag ID. + public RawTaggedData(ushort tag) + { + _tag = tag; + } + + #region ITaggedData Members + + /// + /// Get the ID for this tagged data value. + /// + public ushort TagID + { + get { return _tag; } + set { _tag = value; } + } + + /// + /// Set the data from the raw values provided. + /// + /// The raw data to extract values from. + /// The index to start extracting values from. + /// The number of bytes available. + public void SetData(byte[] data, int offset, int count) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + _data = new byte[count]; + Array.Copy(data, offset, _data, 0, count); + } + + /// + /// Get the binary data representing this instance. + /// + /// The raw binary data representing this instance. + public byte[] GetData() + { + return _data; + } + + #endregion ITaggedData Members + + /// + /// Get /set the binary data representing this instance. + /// + /// The raw binary data representing this instance. + public byte[] Data + { + get { return _data; } + set { _data = value; } + } + + #region Instance Fields + + /// + /// The tag ID for this instance. + /// + private ushort _tag; + + private byte[] _data; + + #endregion Instance Fields + } + + /// + /// Class representing extended unix date time values. + /// + public class ExtendedUnixData : ITaggedData + { + /// + /// Flags indicate which values are included in this instance. + /// + [Flags] + public enum Flags : byte + { + /// + /// The modification time is included + /// + ModificationTime = 0x01, + + /// + /// The access time is included + /// + AccessTime = 0x02, + + /// + /// The create time is included. + /// + CreateTime = 0x04, + } + + #region ITaggedData Members + + /// + /// Get the ID + /// + public ushort TagID + { + get { return 0x5455; } + } + + /// + /// Set the data from the raw values provided. + /// + /// The raw data to extract values from. + /// The index to start extracting values from. + /// The number of bytes available. + public void SetData(byte[] data, int index, int count) + { + using (MemoryStream ms = new MemoryStream(data, index, count, false)) + { + // bit 0 if set, modification time is present + // bit 1 if set, access time is present + // bit 2 if set, creation time is present + + _flags = (Flags)ms.ReadByte(); + if (((_flags & Flags.ModificationTime) != 0)) + { + int iTime = ms.ReadLEInt(); + + _modificationTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + + new TimeSpan(0, 0, 0, iTime, 0); + + // Central-header version is truncated after modification time + if (count <= 5) return; + } + + if ((_flags & Flags.AccessTime) != 0) + { + int iTime = ms.ReadLEInt(); + + _lastAccessTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + + new TimeSpan(0, 0, 0, iTime, 0); + } + + if ((_flags & Flags.CreateTime) != 0) + { + int iTime = ms.ReadLEInt(); + + _createTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + + new TimeSpan(0, 0, 0, iTime, 0); + } + } + } + + /// + /// Get the binary data representing this instance. + /// + /// The raw binary data representing this instance. + public byte[] GetData() + { + using (MemoryStream ms = new MemoryStream()) + { + ms.WriteByte((byte)_flags); // Flags + if ((_flags & Flags.ModificationTime) != 0) + { + TimeSpan span = _modificationTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + var seconds = (int)span.TotalSeconds; + ms.WriteLEInt(seconds); + } + if ((_flags & Flags.AccessTime) != 0) + { + TimeSpan span = _lastAccessTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + var seconds = (int)span.TotalSeconds; + ms.WriteLEInt(seconds); + } + if ((_flags & Flags.CreateTime) != 0) + { + TimeSpan span = _createTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + var seconds = (int)span.TotalSeconds; + ms.WriteLEInt(seconds); + } + return ms.ToArray(); + } + } + + #endregion ITaggedData Members + + /// + /// Test a value to see if is valid and can be represented here. + /// + /// The value to test. + /// Returns true if the value is valid and can be represented; false if not. + /// The standard Unix time is a signed integer data type, directly encoding the Unix time number, + /// which is the number of seconds since 1970-01-01. + /// Being 32 bits means the values here cover a range of about 136 years. + /// The minimum representable time is 1901-12-13 20:45:52, + /// and the maximum representable time is 2038-01-19 03:14:07. + /// + public static bool IsValidValue(DateTime value) + { + return ((value >= new DateTime(1901, 12, 13, 20, 45, 52)) || + (value <= new DateTime(2038, 1, 19, 03, 14, 07))); + } + + /// + /// Get /set the Modification Time + /// + /// + /// + public DateTime ModificationTime + { + get { return _modificationTime; } + set + { + if (!IsValidValue(value)) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + _flags |= Flags.ModificationTime; + _modificationTime = value; + } + } + + /// + /// Get / set the Access Time + /// + /// + /// + public DateTime AccessTime + { + get { return _lastAccessTime; } + set + { + if (!IsValidValue(value)) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + _flags |= Flags.AccessTime; + _lastAccessTime = value; + } + } + + /// + /// Get / Set the Create Time + /// + /// + /// + public DateTime CreateTime + { + get { return _createTime; } + set + { + if (!IsValidValue(value)) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + _flags |= Flags.CreateTime; + _createTime = value; + } + } + + /// + /// Get/set the values to include. + /// + public Flags Include + { + get { return _flags; } + set { _flags = value; } + } + + #region Instance Fields + + private Flags _flags; + private DateTime _modificationTime = new DateTime(1970, 1, 1); + private DateTime _lastAccessTime = new DateTime(1970, 1, 1); + private DateTime _createTime = new DateTime(1970, 1, 1); + + #endregion Instance Fields + } + + /// + /// Class handling NT date time values. + /// + public class NTTaggedData : ITaggedData + { + /// + /// Get the ID for this tagged data value. + /// + public ushort TagID + { + get { return 10; } + } + + /// + /// Set the data from the raw values provided. + /// + /// The raw data to extract values from. + /// The index to start extracting values from. + /// The number of bytes available. + public void SetData(byte[] data, int index, int count) + { + using (MemoryStream ms = new MemoryStream(data, index, count, false)) + { + ms.ReadLEInt(); // Reserved + while (ms.Position < ms.Length) + { + int ntfsTag = ms.ReadLEShort(); + int ntfsLength = ms.ReadLEShort(); + if (ntfsTag == 1) + { + if (ntfsLength >= 24) + { + long lastModificationTicks = ms.ReadLELong(); + _lastModificationTime = DateTime.FromFileTimeUtc(lastModificationTicks); + + long lastAccessTicks = ms.ReadLELong(); + _lastAccessTime = DateTime.FromFileTimeUtc(lastAccessTicks); + + long createTimeTicks = ms.ReadLELong(); + _createTime = DateTime.FromFileTimeUtc(createTimeTicks); + } + break; + } + else + { + // An unknown NTFS tag so simply skip it. + ms.Seek(ntfsLength, SeekOrigin.Current); + } + } + } + } + + /// + /// Get the binary data representing this instance. + /// + /// The raw binary data representing this instance. + public byte[] GetData() + { + using (MemoryStream ms = new MemoryStream()) + { + ms.WriteLEInt(0); // Reserved + ms.WriteLEShort(1); // Tag + ms.WriteLEShort(24); // Length = 3 x 8. + ms.WriteLELong(_lastModificationTime.ToFileTimeUtc()); + ms.WriteLELong(_lastAccessTime.ToFileTimeUtc()); + ms.WriteLELong(_createTime.ToFileTimeUtc()); + return ms.ToArray(); + } + } + + /// + /// Test a valuie to see if is valid and can be represented here. + /// + /// The value to test. + /// Returns true if the value is valid and can be represented; false if not. + /// + /// NTFS filetimes are 64-bit unsigned integers, stored in Intel + /// (least significant byte first) byte order. They determine the + /// number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch", + /// which is "01-Jan-1601 00:00:00 UTC". 28 May 60056 is the upper limit + /// + public static bool IsValidValue(DateTime value) + { + bool result = true; + try + { + value.ToFileTimeUtc(); + } + catch + { + result = false; + } + return result; + } + + /// + /// Get/set the last modification time. + /// + public DateTime LastModificationTime + { + get { return _lastModificationTime; } + set + { + if (!IsValidValue(value)) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + _lastModificationTime = value; + } + } + + /// + /// Get /set the create time + /// + public DateTime CreateTime + { + get { return _createTime; } + set + { + if (!IsValidValue(value)) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + _createTime = value; + } + } + + /// + /// Get /set the last access time. + /// + public DateTime LastAccessTime + { + get { return _lastAccessTime; } + set + { + if (!IsValidValue(value)) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + _lastAccessTime = value; + } + } + + #region Instance Fields + + private DateTime _lastAccessTime = DateTime.FromFileTimeUtc(0); + private DateTime _lastModificationTime = DateTime.FromFileTimeUtc(0); + private DateTime _createTime = DateTime.FromFileTimeUtc(0); + + #endregion Instance Fields + } + + /// + /// A factory that creates tagged data instances. + /// + internal interface ITaggedDataFactory + { + /// + /// Get data for a specific tag value. + /// + /// The tag ID to find. + /// The data to search. + /// The offset to begin extracting data from. + /// The number of bytes to extract. + /// The located value found, or null if not found. + ITaggedData Create(short tag, byte[] data, int offset, int count); + } + + /// + /// + /// A class to handle the extra data field for Zip entries + /// + /// + /// Extra data contains 0 or more values each prefixed by a header tag and length. + /// They contain zero or more bytes of actual data. + /// The data is held internally using a copy on write strategy. This is more efficient but + /// means that for extra data created by passing in data can have the values modified by the caller + /// in some circumstances. + /// + sealed public class ZipExtraData : IDisposable + { + #region Constructors + + /// + /// Initialise a default instance. + /// + public ZipExtraData() + { + Clear(); + } + + /// + /// Initialise with known extra data. + /// + /// The extra data. + public ZipExtraData(byte[] data) + { + if (data == null) + { + _data = Empty.Array(); + } + else + { + _data = data; + } + } + + #endregion Constructors + + /// + /// Get the raw extra data value + /// + /// Returns the raw byte[] extra data this instance represents. + public byte[] GetEntryData() + { + if (Length > ushort.MaxValue) + { + throw new ZipException("Data exceeds maximum length"); + } + + return (byte[])_data.Clone(); + } + + /// + /// Clear the stored data. + /// + public void Clear() + { + if ((_data == null) || (_data.Length != 0)) + { + _data = Empty.Array(); + } + } + + /// + /// Gets the current extra data length. + /// + public int Length + { + get { return _data.Length; } + } + + /// + /// Get a read-only for the associated tag. + /// + /// The tag to locate data for. + /// Returns a containing tag data or null if no tag was found. + public Stream GetStreamForTag(int tag) + { + Stream result = null; + if (Find(tag)) + { + result = new MemoryStream(_data, _index, _readValueLength, false); + } + return result; + } + + /// + /// Get the tagged data for a tag. + /// + /// The tag to search for. + /// Returns a tagged value or null if none found. + public T GetData() + where T : class, ITaggedData, new() + { + T result = new T(); + if (Find(result.TagID)) + { + result.SetData(_data, _readValueStart, _readValueLength); + return result; + } + else return null; + } + + /// + /// Get the length of the last value found by + /// + /// This is only valid if has previously returned true. + public int ValueLength + { + get { return _readValueLength; } + } + + /// + /// Get the index for the current read value. + /// + /// This is only valid if has previously returned true. + /// Initially the result will be the index of the first byte of actual data. The value is updated after calls to + /// , and . + public int CurrentReadIndex + { + get { return _index; } + } + + /// + /// Get the number of bytes remaining to be read for the current value; + /// + public int UnreadCount + { + get + { + if ((_readValueStart > _data.Length) || + (_readValueStart < 4)) + { + throw new ZipException("Find must be called before calling a Read method"); + } + + return _readValueStart + _readValueLength - _index; + } + } + + /// + /// Find an extra data value + /// + /// The identifier for the value to find. + /// Returns true if the value was found; false otherwise. + public bool Find(int headerID) + { + _readValueStart = _data.Length; + _readValueLength = 0; + _index = 0; + + int localLength = _readValueStart; + int localTag = headerID - 1; + + // Trailing bytes that cant make up an entry (as there arent enough + // bytes for a tag and length) are ignored! + while ((localTag != headerID) && (_index < _data.Length - 3)) + { + localTag = ReadShortInternal(); + localLength = ReadShortInternal(); + if (localTag != headerID) + { + _index += localLength; + } + } + + bool result = (localTag == headerID) && ((_index + localLength) <= _data.Length); + + if (result) + { + _readValueStart = _index; + _readValueLength = localLength; + } + + return result; + } + + /// + /// Add a new entry to extra data. + /// + /// The value to add. + public void AddEntry(ITaggedData taggedData) + { + if (taggedData == null) + { + throw new ArgumentNullException(nameof(taggedData)); + } + AddEntry(taggedData.TagID, taggedData.GetData()); + } + + /// + /// Add a new entry to extra data + /// + /// The ID for this entry. + /// The data to add. + /// If the ID already exists its contents are replaced. + public void AddEntry(int headerID, byte[] fieldData) + { + if ((headerID > ushort.MaxValue) || (headerID < 0)) + { + throw new ArgumentOutOfRangeException(nameof(headerID)); + } + + int addLength = (fieldData == null) ? 0 : fieldData.Length; + + if (addLength > ushort.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(fieldData), "exceeds maximum length"); + } + + // Test for new length before adjusting data. + int newLength = _data.Length + addLength + 4; + + if (Find(headerID)) + { + newLength -= (ValueLength + 4); + } + + if (newLength > ushort.MaxValue) + { + throw new ZipException("Data exceeds maximum length"); + } + + Delete(headerID); + + byte[] newData = new byte[newLength]; + _data.CopyTo(newData, 0); + int index = _data.Length; + _data = newData; + SetShort(ref index, headerID); + SetShort(ref index, addLength); + if (fieldData != null) + { + fieldData.CopyTo(newData, index); + } + } + + /// + /// Start adding a new entry. + /// + /// Add data using , , , or . + /// The new entry is completed and actually added by calling + /// + public void StartNewEntry() + { + _newEntry = new MemoryStream(); + } + + /// + /// Add entry data added since using the ID passed. + /// + /// The identifier to use for this entry. + public void AddNewEntry(int headerID) + { + byte[] newData = _newEntry.ToArray(); + _newEntry = null; + AddEntry(headerID, newData); + } + + /// + /// Add a byte of data to the pending new entry. + /// + /// The byte to add. + /// + public void AddData(byte data) + { + _newEntry.WriteByte(data); + } + + /// + /// Add data to a pending new entry. + /// + /// The data to add. + /// + public void AddData(byte[] data) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + _newEntry.Write(data, 0, data.Length); + } + + /// + /// Add a short value in little endian order to the pending new entry. + /// + /// The data to add. + /// + public void AddLeShort(int toAdd) + { + unchecked + { + _newEntry.WriteByte((byte)toAdd); + _newEntry.WriteByte((byte)(toAdd >> 8)); + } + } + + /// + /// Add an integer value in little endian order to the pending new entry. + /// + /// The data to add. + /// + public void AddLeInt(int toAdd) + { + unchecked + { + AddLeShort((short)toAdd); + AddLeShort((short)(toAdd >> 16)); + } + } + + /// + /// Add a long value in little endian order to the pending new entry. + /// + /// The data to add. + /// + public void AddLeLong(long toAdd) + { + unchecked + { + AddLeInt((int)(toAdd & 0xffffffff)); + AddLeInt((int)(toAdd >> 32)); + } + } + + /// + /// Delete an extra data field. + /// + /// The identifier of the field to delete. + /// Returns true if the field was found and deleted. + public bool Delete(int headerID) + { + bool result = false; + + if (Find(headerID)) + { + result = true; + int trueStart = _readValueStart - 4; + + byte[] newData = new byte[_data.Length - (ValueLength + 4)]; + Array.Copy(_data, 0, newData, 0, trueStart); + + int trueEnd = trueStart + ValueLength + 4; + Array.Copy(_data, trueEnd, newData, trueStart, _data.Length - trueEnd); + _data = newData; + } + return result; + } + + #region Reading Support + + /// + /// Read a long in little endian form from the last found data value + /// + /// Returns the long value read. + public long ReadLong() + { + ReadCheck(8); + return (ReadInt() & 0xffffffff) | (((long)ReadInt()) << 32); + } + + /// + /// Read an integer in little endian form from the last found data value. + /// + /// Returns the integer read. + public int ReadInt() + { + ReadCheck(4); + + int result = _data[_index] + (_data[_index + 1] << 8) + + (_data[_index + 2] << 16) + (_data[_index + 3] << 24); + _index += 4; + return result; + } + + /// + /// Read a short value in little endian form from the last found data value. + /// + /// Returns the short value read. + public int ReadShort() + { + ReadCheck(2); + int result = _data[_index] + (_data[_index + 1] << 8); + _index += 2; + return result; + } + + /// + /// Read a byte from an extra data + /// + /// The byte value read or -1 if the end of data has been reached. + public int ReadByte() + { + int result = -1; + if ((_index < _data.Length) && (_readValueStart + _readValueLength > _index)) + { + result = _data[_index]; + _index += 1; + } + return result; + } + + /// + /// Skip data during reading. + /// + /// The number of bytes to skip. + public void Skip(int amount) + { + ReadCheck(amount); + _index += amount; + } + + private void ReadCheck(int length) + { + if ((_readValueStart > _data.Length) || + (_readValueStart < 4)) + { + throw new ZipException("Find must be called before calling a Read method"); + } + + if (_index > _readValueStart + _readValueLength - length) + { + throw new ZipException("End of extra data"); + } + + if (_index + length < 4) + { + throw new ZipException("Cannot read before start of tag"); + } + } + + /// + /// Internal form of that reads data at any location. + /// + /// Returns the short value read. + private int ReadShortInternal() + { + if (_index > _data.Length - 2) + { + throw new ZipException("End of extra data"); + } + + int result = _data[_index] + (_data[_index + 1] << 8); + _index += 2; + return result; + } + + private void SetShort(ref int index, int source) + { + _data[index] = (byte)source; + _data[index + 1] = (byte)(source >> 8); + index += 2; + } + + #endregion Reading Support + + #region IDisposable Members + + /// + /// Dispose of this instance. + /// + public void Dispose() + { + if (_newEntry != null) + { + _newEntry.Dispose(); + } + } + + #endregion IDisposable Members + + #region Instance Fields + + private int _index; + private int _readValueStart; + private int _readValueLength; + + private MemoryStream _newEntry; + private byte[] _data; + + #endregion Instance Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/ICSharpCode.SharpZipLib/Zip/ZipFile.cs new file mode 100644 index 000000000000..ea1fa638ec7e --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipFile.cs @@ -0,0 +1,5059 @@ +using ICSharpCode.SharpZipLib.Checksum; +using ICSharpCode.SharpZipLib.Core; +using ICSharpCode.SharpZipLib.Encryption; +using ICSharpCode.SharpZipLib.Zip.Compression; +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace ICSharpCode.SharpZipLib.Zip +{ + #region Keys Required Event Args + + /// + /// Arguments used with KeysRequiredEvent + /// + public class KeysRequiredEventArgs : EventArgs + { + #region Constructors + + /// + /// Initialise a new instance of + /// + /// The name of the file for which keys are required. + public KeysRequiredEventArgs(string name) + { + fileName = name; + } + + /// + /// Initialise a new instance of + /// + /// The name of the file for which keys are required. + /// The current key value. + public KeysRequiredEventArgs(string name, byte[] keyValue) + { + fileName = name; + key = keyValue; + } + + #endregion Constructors + + #region Properties + + /// + /// Gets the name of the file for which keys are required. + /// + public string FileName + { + get { return fileName; } + } + + /// + /// Gets or sets the key value + /// + public byte[] Key + { + get { return key; } + set { key = value; } + } + + #endregion Properties + + #region Instance Fields + + private readonly string fileName; + private byte[] key; + + #endregion Instance Fields + } + + #endregion Keys Required Event Args + + #region Test Definitions + + /// + /// The strategy to apply to testing. + /// + public enum TestStrategy + { + /// + /// Find the first error only. + /// + FindFirstError, + + /// + /// Find all possible errors. + /// + FindAllErrors, + } + + /// + /// The operation in progress reported by a during testing. + /// + /// TestArchive + public enum TestOperation + { + /// + /// Setting up testing. + /// + Initialising, + + /// + /// Testing an individual entries header + /// + EntryHeader, + + /// + /// Testing an individual entries data + /// + EntryData, + + /// + /// Testing an individual entry has completed. + /// + EntryComplete, + + /// + /// Running miscellaneous tests + /// + MiscellaneousTests, + + /// + /// Testing is complete + /// + Complete, + } + + /// + /// Status returned by during testing. + /// + /// TestArchive + public class TestStatus + { + #region Constructors + + /// + /// Initialise a new instance of + /// + /// The this status applies to. + public TestStatus(ZipFile file) + { + file_ = file; + } + + #endregion Constructors + + #region Properties + + /// + /// Get the current in progress. + /// + public TestOperation Operation + { + get { return operation_; } + } + + /// + /// Get the this status is applicable to. + /// + public ZipFile File + { + get { return file_; } + } + + /// + /// Get the current/last entry tested. + /// + public ZipEntry Entry + { + get { return entry_; } + } + + /// + /// Get the number of errors detected so far. + /// + public int ErrorCount + { + get { return errorCount_; } + } + + /// + /// Get the number of bytes tested so far for the current entry. + /// + public long BytesTested + { + get { return bytesTested_; } + } + + /// + /// Get a value indicating whether the last entry test was valid. + /// + public bool EntryValid + { + get { return entryValid_; } + } + + #endregion Properties + + #region Internal API + + internal void AddError() + { + errorCount_++; + entryValid_ = false; + } + + internal void SetOperation(TestOperation operation) + { + operation_ = operation; + } + + internal void SetEntry(ZipEntry entry) + { + entry_ = entry; + entryValid_ = true; + bytesTested_ = 0; + } + + internal void SetBytesTested(long value) + { + bytesTested_ = value; + } + + #endregion Internal API + + #region Instance Fields + + private readonly ZipFile file_; + private ZipEntry entry_; + private bool entryValid_; + private int errorCount_; + private long bytesTested_; + private TestOperation operation_; + + #endregion Instance Fields + } + + /// + /// Delegate invoked during testing if supplied indicating current progress and status. + /// + /// If the message is non-null an error has occured. If the message is null + /// the operation as found in status has started. + public delegate void ZipTestResultHandler(TestStatus status, string message); + + #endregion Test Definitions + + #region Update Definitions + + /// + /// The possible ways of applying updates to an archive. + /// + public enum FileUpdateMode + { + /// + /// Perform all updates on temporary files ensuring that the original file is saved. + /// + Safe, + + /// + /// Update the archive directly, which is faster but less safe. + /// + Direct, + } + + #endregion Update Definitions + + #region ZipFile Class + + /// + /// This class represents a Zip archive. You can ask for the contained + /// entries, or get an input stream for a file entry. The entry is + /// automatically decompressed. + /// + /// You can also update the archive adding or deleting entries. + /// + /// This class is thread safe for input: You can open input streams for arbitrary + /// entries in different threads. + ///
+ ///
Author of the original java version : Jochen Hoenicke + ///
+ /// + /// + /// using System; + /// using System.Text; + /// using System.Collections; + /// using System.IO; + /// + /// using ICSharpCode.SharpZipLib.Zip; + /// + /// class MainClass + /// { + /// static public void Main(string[] args) + /// { + /// using (ZipFile zFile = new ZipFile(args[0])) { + /// Console.WriteLine("Listing of : " + zFile.Name); + /// Console.WriteLine(""); + /// Console.WriteLine("Raw Size Size Date Time Name"); + /// Console.WriteLine("-------- -------- -------- ------ ---------"); + /// foreach (ZipEntry e in zFile) { + /// if ( e.IsFile ) { + /// DateTime d = e.DateTime; + /// Console.WriteLine("{0, -10}{1, -10}{2} {3} {4}", e.Size, e.CompressedSize, + /// d.ToString("dd-MM-yy"), d.ToString("HH:mm"), + /// e.Name); + /// } + /// } + /// } + /// } + /// } + /// + /// + public class ZipFile : IEnumerable, IDisposable + { + #region KeyHandling + + /// + /// Delegate for handling keys/password setting during compression/decompression. + /// + public delegate void KeysRequiredEventHandler( + object sender, + KeysRequiredEventArgs e + ); + + /// + /// Event handler for handling encryption keys. + /// + public KeysRequiredEventHandler KeysRequired; + + /// + /// Handles getting of encryption keys when required. + /// + /// The file for which encryption keys are required. + private void OnKeysRequired(string fileName) + { + if (KeysRequired != null) + { + var krea = new KeysRequiredEventArgs(fileName, key); + KeysRequired(this, krea); + key = krea.Key; + } + } + + /// + /// Get/set the encryption key value. + /// + private byte[] Key + { + get { return key; } + set { key = value; } + } + + /// + /// Password to be used for encrypting/decrypting files. + /// + /// Set to null if no password is required. + public string Password + { + set + { + if (string.IsNullOrEmpty(value)) + { + key = null; + } + else + { + key = PkzipClassic.GenerateKeys(ZipCryptoEncoding.GetBytes(value)); + } + + rawPassword_ = value; + } + } + + /// + /// Get a value indicating whether encryption keys are currently available. + /// + private bool HaveKeys + { + get { return key != null; } + } + + #endregion KeyHandling + + #region Constructors + + /// + /// Opens a Zip file with the given name for reading. + /// + /// The name of the file to open. + /// + /// The argument supplied is null. + /// + /// An i/o error occurs + /// + /// + /// The file doesn't contain a valid zip archive. + /// + public ZipFile(string name, StringCodec stringCodec = null) + { + name_ = name ?? throw new ArgumentNullException(nameof(name)); + + baseStream_ = File.Open(name, FileMode.Open, FileAccess.Read, FileShare.Read); + isStreamOwner = true; + + if (stringCodec != null) + { + _stringCodec = stringCodec; + } + + try + { + ReadEntries(); + } + catch + { + DisposeInternal(true); + throw; + } + } + + /// + /// Opens a Zip file reading the given . + /// + /// The to read archive data from. + /// The supplied argument is null. + /// + /// An i/o error occurs. + /// + /// + /// The file doesn't contain a valid zip archive. + /// + public ZipFile(FileStream file) : + this(file, false) + { + + } + + /// + /// Opens a Zip file reading the given . + /// + /// The to read archive data from. + /// true to leave the file open when the ZipFile is disposed, false to dispose of it + /// The supplied argument is null. + /// + /// An i/o error occurs. + /// + /// + /// The file doesn't contain a valid zip archive. + /// + public ZipFile(FileStream file, bool leaveOpen) + { + if (file == null) + { + throw new ArgumentNullException(nameof(file)); + } + + if (!file.CanSeek) + { + throw new ArgumentException("Stream is not seekable", nameof(file)); + } + + baseStream_ = file; + name_ = file.Name; + isStreamOwner = !leaveOpen; + + try + { + ReadEntries(); + } + catch + { + DisposeInternal(true); + throw; + } + } + + /// + /// Opens a Zip file reading the given . + /// + /// The to read archive data from. + /// + /// An i/o error occurs + /// + /// + /// The stream doesn't contain a valid zip archive.
+ ///
+ /// + /// The stream doesnt support seeking. + /// + /// + /// The stream argument is null. + /// + public ZipFile(Stream stream) : + this(stream, false) + { + + } + + /// + /// Opens a Zip file reading the given . + /// + /// The to read archive data from. + /// true to leave the stream open when the ZipFile is disposed, false to dispose of it + /// + /// An i/o error occurs + /// + /// + /// The stream doesn't contain a valid zip archive.
+ ///
+ /// + /// The stream doesnt support seeking. + /// + /// + /// The stream argument is null. + /// + public ZipFile(Stream stream, bool leaveOpen) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!stream.CanSeek) + { + throw new ArgumentException("Stream is not seekable", nameof(stream)); + } + + baseStream_ = stream; + isStreamOwner = !leaveOpen; + + if (baseStream_.Length > 0) + { + try + { + ReadEntries(); + } + catch + { + DisposeInternal(true); + throw; + } + } + else + { + entries_ = Empty.Array(); + isNewArchive_ = true; + } + } + + /// + /// Initialises a default instance with no entries and no file storage. + /// + internal ZipFile() + { + entries_ = Empty.Array(); + isNewArchive_ = true; + } + + #endregion Constructors + + #region Destructors and Closing + + /// + /// Finalize this instance. + /// + ~ZipFile() + { + Dispose(false); + } + + /// + /// Closes the ZipFile. If the stream is owned then this also closes the underlying input stream. + /// Once closed, no further instance methods should be called. + /// + /// + /// An i/o error occurs. + /// + public void Close() + { + DisposeInternal(true); + GC.SuppressFinalize(this); + } + + #endregion Destructors and Closing + + #region Creators + + /// + /// Create a new whose data will be stored in a file. + /// + /// The name of the archive to create. + /// Returns the newly created + /// is null + public static ZipFile Create(string fileName) + { + if (fileName == null) + { + throw new ArgumentNullException(nameof(fileName)); + } + + FileStream fs = File.Create(fileName); + + return new ZipFile + { + name_ = fileName, + baseStream_ = fs, + isStreamOwner = true + }; + } + + /// + /// Create a new whose data will be stored on a stream. + /// + /// The stream providing data storage. + /// Returns the newly created + /// is null + /// doesnt support writing. + public static ZipFile Create(Stream outStream) + { + if (outStream == null) + { + throw new ArgumentNullException(nameof(outStream)); + } + + if (!outStream.CanWrite) + { + throw new ArgumentException("Stream is not writeable", nameof(outStream)); + } + + if (!outStream.CanSeek) + { + throw new ArgumentException("Stream is not seekable", nameof(outStream)); + } + + var result = new ZipFile + { + baseStream_ = outStream + }; + return result; + } + + #endregion Creators + + #region Properties + + /// + /// Get/set a flag indicating if the underlying stream is owned by the ZipFile instance. + /// If the flag is true then the stream will be closed when Close is called. + /// + /// + /// The default value is true in all cases. + /// + public bool IsStreamOwner + { + get { return isStreamOwner; } + set { isStreamOwner = value; } + } + + /// + /// Get a value indicating whether + /// this archive is embedded in another file or not. + /// + public bool IsEmbeddedArchive + { + // Not strictly correct in all circumstances currently + get { return offsetOfFirstEntry > 0; } + } + + /// + /// Get a value indicating that this archive is a new one. + /// + public bool IsNewArchive + { + get { return isNewArchive_; } + } + + /// + /// Gets the comment for the zip file. + /// + public string ZipFileComment + { + get { return comment_; } + } + + /// + /// Gets the name of this zip file. + /// + public string Name + { + get { return name_; } + } + + /// + /// Gets the number of entries in this zip file. + /// + /// + /// The Zip file has been closed. + /// + [Obsolete("Use the Count property instead")] + public int Size + { + get + { + return entries_.Length; + } + } + + /// + /// Get the number of entries contained in this . + /// + public long Count + { + get + { + return entries_.Length; + } + } + + /// + /// Indexer property for ZipEntries + /// + [System.Runtime.CompilerServices.IndexerNameAttribute("EntryByIndex")] + public ZipEntry this[int index] + { + get + { + return (ZipEntry)entries_[index].Clone(); + } + } + + + /// + public Encoding ZipCryptoEncoding + { + get => _stringCodec.ZipCryptoEncoding; + set => _stringCodec.ZipCryptoEncoding = value; + } + + /// + public StringCodec StringCodec + { + get => _stringCodec; + set => _stringCodec = value; + } + + #endregion Properties + + #region Input Handling + + /// + /// Gets an enumerator for the Zip entries in this Zip file. + /// + /// Returns an for this archive. + /// + /// The Zip file has been closed. + /// + public IEnumerator GetEnumerator() + { + if (isDisposed_) + { + throw new ObjectDisposedException("ZipFile"); + } + + return new ZipEntryEnumerator(entries_); + } + + /// + /// Return the index of the entry with a matching name + /// + /// Entry name to find + /// If true the comparison is case insensitive + /// The index position of the matching entry or -1 if not found + /// + /// The Zip file has been closed. + /// + public int FindEntry(string name, bool ignoreCase) + { + if (isDisposed_) + { + throw new ObjectDisposedException("ZipFile"); + } + + // TODO: This will be slow as the next ice age for huge archives! + for (int i = 0; i < entries_.Length; i++) + { + if (string.Compare(name, entries_[i].Name, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0) + { + return i; + } + } + return -1; + } + + /// + /// Searches for a zip entry in this archive with the given name. + /// String comparisons are case insensitive + /// + /// + /// The name to find. May contain directory components separated by slashes ('/'). + /// + /// + /// A clone of the zip entry, or null if no entry with that name exists. + /// + /// + /// The Zip file has been closed. + /// + public ZipEntry GetEntry(string name) + { + if (isDisposed_) + { + throw new ObjectDisposedException("ZipFile"); + } + + int index = FindEntry(name, true); + return (index >= 0) ? (ZipEntry)entries_[index].Clone() : null; + } + + /// + /// Gets an input stream for reading the given zip entry data in an uncompressed form. + /// Normally the should be an entry returned by GetEntry(). + /// + /// The to obtain a data for + /// An input containing data for this + /// + /// The ZipFile has already been closed + /// + /// + /// The compression method for the entry is unknown + /// + /// + /// The entry is not found in the ZipFile + /// + public Stream GetInputStream(ZipEntry entry) + { + if (entry == null) + { + throw new ArgumentNullException(nameof(entry)); + } + + if (isDisposed_) + { + throw new ObjectDisposedException("ZipFile"); + } + + long index = entry.ZipFileIndex; + if ((index < 0) || (index >= entries_.Length) || (entries_[index].Name != entry.Name)) + { + index = FindEntry(entry.Name, true); + if (index < 0) + { + throw new ZipException("Entry cannot be found"); + } + } + return GetInputStream(index); + } + + /// + /// Creates an input stream reading a zip entry + /// + /// The index of the entry to obtain an input stream for. + /// + /// An input containing data for this + /// + /// + /// The ZipFile has already been closed + /// + /// + /// The compression method for the entry is unknown + /// + /// + /// The entry is not found in the ZipFile + /// + public Stream GetInputStream(long entryIndex) + { + if (isDisposed_) + { + throw new ObjectDisposedException("ZipFile"); + } + + long start = LocateEntry(entries_[entryIndex]); + CompressionMethod method = entries_[entryIndex].CompressionMethod; + Stream result = new PartialInputStream(this, start, entries_[entryIndex].CompressedSize); + + if (entries_[entryIndex].IsCrypted == true) + { + result = CreateAndInitDecryptionStream(result, entries_[entryIndex]); + if (result == null) + { + throw new ZipException("Unable to decrypt this entry"); + } + } + + switch (method) + { + case CompressionMethod.Stored: + // read as is. + break; + + case CompressionMethod.Deflated: + // No need to worry about ownership and closing as underlying stream close does nothing. + result = new InflaterInputStream(result, new Inflater(true)); + break; + + case CompressionMethod.BZip2: + result = new BZip2.BZip2InputStream(result); + break; + + default: + throw new ZipException("Unsupported compression method " + method); + } + + return result; + } + + #endregion Input Handling + + #region Archive Testing + + /// + /// Test an archive for integrity/validity + /// + /// Perform low level data Crc check + /// true if all tests pass, false otherwise + /// Testing will terminate on the first error found. + public bool TestArchive(bool testData) + { + return TestArchive(testData, TestStrategy.FindFirstError, null); + } + + /// + /// Test an archive for integrity/validity + /// + /// Perform low level data Crc check + /// The to apply. + /// The handler to call during testing. + /// true if all tests pass, false otherwise + /// The object has already been closed. + public bool TestArchive(bool testData, TestStrategy strategy, ZipTestResultHandler resultHandler) + { + if (isDisposed_) + { + throw new ObjectDisposedException("ZipFile"); + } + + var status = new TestStatus(this); + + resultHandler?.Invoke(status, null); + + HeaderTest test = testData ? (HeaderTest.Header | HeaderTest.Extract) : HeaderTest.Header; + + bool testing = true; + + try + { + int entryIndex = 0; + + while (testing && (entryIndex < Count)) + { + if (resultHandler != null) + { + status.SetEntry(this[entryIndex]); + status.SetOperation(TestOperation.EntryHeader); + resultHandler(status, null); + } + + try + { + TestLocalHeader(this[entryIndex], test); + } + catch (ZipException ex) + { + status.AddError(); + + resultHandler?.Invoke(status, $"Exception during test - '{ex.Message}'"); + + testing &= strategy != TestStrategy.FindFirstError; + } + + if (testing && testData && this[entryIndex].IsFile) + { + // Don't check CRC for AES encrypted archives + var checkCRC = this[entryIndex].AESKeySize == 0; + + if (resultHandler != null) + { + status.SetOperation(TestOperation.EntryData); + resultHandler(status, null); + } + + var crc = new Crc32(); + + using (Stream entryStream = this.GetInputStream(this[entryIndex])) + { + byte[] buffer = new byte[4096]; + long totalBytes = 0; + int bytesRead; + while ((bytesRead = entryStream.Read(buffer, 0, buffer.Length)) > 0) + { + if (checkCRC) + { + crc.Update(new ArraySegment(buffer, 0, bytesRead)); + } + + if (resultHandler != null) + { + totalBytes += bytesRead; + status.SetBytesTested(totalBytes); + resultHandler(status, null); + } + } + } + + if (checkCRC && this[entryIndex].Crc != crc.Value) + { + status.AddError(); + + resultHandler?.Invoke(status, "CRC mismatch"); + + testing &= strategy != TestStrategy.FindFirstError; + } + + if ((this[entryIndex].Flags & (int)GeneralBitFlags.Descriptor) != 0) + { + var data = new DescriptorData(); + ZipFormat.ReadDataDescriptor(baseStream_, this[entryIndex].LocalHeaderRequiresZip64, data); + if (checkCRC && this[entryIndex].Crc != data.Crc) + { + status.AddError(); + resultHandler?.Invoke(status, "Descriptor CRC mismatch"); + } + + if (this[entryIndex].CompressedSize != data.CompressedSize) + { + status.AddError(); + resultHandler?.Invoke(status, "Descriptor compressed size mismatch"); + } + + if (this[entryIndex].Size != data.Size) + { + status.AddError(); + resultHandler?.Invoke(status, "Descriptor size mismatch"); + } + } + } + + if (resultHandler != null) + { + status.SetOperation(TestOperation.EntryComplete); + resultHandler(status, null); + } + + entryIndex += 1; + } + + if (resultHandler != null) + { + status.SetOperation(TestOperation.MiscellaneousTests); + resultHandler(status, null); + } + + // TODO: the 'Corrina Johns' test where local headers are missing from + // the central directory. They are therefore invisible to many archivers. + } + catch (Exception ex) + { + status.AddError(); + + resultHandler?.Invoke(status, $"Exception during test - '{ex.Message}'"); + } + + if (resultHandler != null) + { + status.SetOperation(TestOperation.Complete); + status.SetEntry(null); + resultHandler(status, null); + } + + return (status.ErrorCount == 0); + } + + [Flags] + private enum HeaderTest + { + Extract = 0x01, // Check that this header represents an entry whose data can be extracted + Header = 0x02, // Check that this header contents are valid + } + + /// + /// Test a local header against that provided from the central directory + /// + /// + /// The entry to test against + /// + /// The type of tests to carry out. + /// The offset of the entries data in the file + private long TestLocalHeader(ZipEntry entry, HeaderTest tests) + { + lock (baseStream_) + { + bool testHeader = (tests & HeaderTest.Header) != 0; + bool testData = (tests & HeaderTest.Extract) != 0; + + var entryAbsOffset = offsetOfFirstEntry + entry.Offset; + + baseStream_.Seek(entryAbsOffset, SeekOrigin.Begin); + var signature = (int)ReadLEUint(); + + if (signature != ZipConstants.LocalHeaderSignature) + { + throw new ZipException(string.Format("Wrong local header signature at 0x{0:x}, expected 0x{1:x8}, actual 0x{2:x8}", + entryAbsOffset, ZipConstants.LocalHeaderSignature, signature)); + } + + var extractVersion = (short)(ReadLEUshort() & 0x00ff); + var localFlags = (short)ReadLEUshort(); + var compressionMethod = (short)ReadLEUshort(); + var fileTime = (short)ReadLEUshort(); + var fileDate = (short)ReadLEUshort(); + uint crcValue = ReadLEUint(); + long compressedSize = ReadLEUint(); + long size = ReadLEUint(); + int storedNameLength = ReadLEUshort(); + int extraDataLength = ReadLEUshort(); + + byte[] nameData = new byte[storedNameLength]; + StreamUtils.ReadFully(baseStream_, nameData); + + byte[] extraData = new byte[extraDataLength]; + StreamUtils.ReadFully(baseStream_, extraData); + + var localExtraData = new ZipExtraData(extraData); + + // Extra data / zip64 checks + if (localExtraData.Find(1)) + { + // 2010-03-04 Forum 10512: removed checks for version >= ZipConstants.VersionZip64 + // and size or compressedSize = MaxValue, due to rogue creators. + + size = localExtraData.ReadLong(); + compressedSize = localExtraData.ReadLong(); + + if ((localFlags & (int)GeneralBitFlags.Descriptor) != 0) + { + // These may be valid if patched later + if ((size != -1) && (size != entry.Size)) + { + throw new ZipException("Size invalid for descriptor"); + } + + if ((compressedSize != -1) && (compressedSize != entry.CompressedSize)) + { + throw new ZipException("Compressed size invalid for descriptor"); + } + } + } + else + { + // No zip64 extra data but entry requires it. + if ((extractVersion >= ZipConstants.VersionZip64) && + (((uint)size == uint.MaxValue) || ((uint)compressedSize == uint.MaxValue))) + { + throw new ZipException("Required Zip64 extended information missing"); + } + } + + if (testData) + { + if (entry.IsFile) + { + if (!entry.IsCompressionMethodSupported()) + { + throw new ZipException("Compression method not supported"); + } + + if ((extractVersion > ZipConstants.VersionMadeBy) + || ((extractVersion > 20) && (extractVersion < ZipConstants.VersionZip64))) + { + throw new ZipException(string.Format("Version required to extract this entry not supported ({0})", extractVersion)); + } + + if ((localFlags & (int)(GeneralBitFlags.Patched | GeneralBitFlags.StrongEncryption | GeneralBitFlags.EnhancedCompress | GeneralBitFlags.HeaderMasked)) != 0) + { + throw new ZipException("The library does not support the zip version required to extract this entry"); + } + } + } + + if (testHeader) + { + if ((extractVersion <= 63) && // Ignore later versions as we dont know about them.. + (extractVersion != 10) && + (extractVersion != 11) && + (extractVersion != 20) && + (extractVersion != 21) && + (extractVersion != 25) && + (extractVersion != 27) && + (extractVersion != 45) && + (extractVersion != 46) && + (extractVersion != 50) && + (extractVersion != 51) && + (extractVersion != 52) && + (extractVersion != 61) && + (extractVersion != 62) && + (extractVersion != 63) + ) + { + throw new ZipException(string.Format("Version required to extract this entry is invalid ({0})", extractVersion)); + } + + var localEncoding = _stringCodec.ZipInputEncoding(localFlags); + + // Local entry flags dont have reserved bit set on. + if ((localFlags & (int)(GeneralBitFlags.ReservedPKware4 | GeneralBitFlags.ReservedPkware14 | GeneralBitFlags.ReservedPkware15)) != 0) + { + throw new ZipException("Reserved bit flags cannot be set."); + } + + // Encryption requires extract version >= 20 + if (((localFlags & (int)GeneralBitFlags.Encrypted) != 0) && (extractVersion < 20)) + { + throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0})", extractVersion)); + } + + // Strong encryption requires encryption flag to be set and extract version >= 50. + if ((localFlags & (int)GeneralBitFlags.StrongEncryption) != 0) + { + if ((localFlags & (int)GeneralBitFlags.Encrypted) == 0) + { + throw new ZipException("Strong encryption flag set but encryption flag is not set"); + } + + if (extractVersion < 50) + { + throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0})", extractVersion)); + } + } + + // Patched entries require extract version >= 27 + if (((localFlags & (int)GeneralBitFlags.Patched) != 0) && (extractVersion < 27)) + { + throw new ZipException(string.Format("Patched data requires higher version than ({0})", extractVersion)); + } + + // Central header flags match local entry flags. + if (localFlags != entry.Flags) + { + throw new ZipException("Central header/local header flags mismatch"); + } + + // Central header compression method matches local entry + if (entry.CompressionMethodForHeader != (CompressionMethod)compressionMethod) + { + throw new ZipException("Central header/local header compression method mismatch"); + } + + if (entry.Version != extractVersion) + { + throw new ZipException("Extract version mismatch"); + } + + // Strong encryption and extract version match + if ((localFlags & (int)GeneralBitFlags.StrongEncryption) != 0) + { + if (extractVersion < 62) + { + throw new ZipException("Strong encryption flag set but version not high enough"); + } + } + + if ((localFlags & (int)GeneralBitFlags.HeaderMasked) != 0) + { + if ((fileTime != 0) || (fileDate != 0)) + { + throw new ZipException("Header masked set but date/time values non-zero"); + } + } + + if ((localFlags & (int)GeneralBitFlags.Descriptor) == 0) + { + if (crcValue != (uint)entry.Crc) + { + throw new ZipException("Central header/local header crc mismatch"); + } + } + + // Crc valid for empty entry. + // This will also apply to streamed entries where size isnt known and the header cant be patched + if ((size == 0) && (compressedSize == 0)) + { + if (crcValue != 0) + { + throw new ZipException("Invalid CRC for empty entry"); + } + } + + // TODO: make test more correct... can't compare lengths as was done originally as this can fail for MBCS strings + // Assuming a code page at this point is not valid? Best is to store the name length in the ZipEntry probably + if (entry.Name.Length > storedNameLength) + { + throw new ZipException("File name length mismatch"); + } + + // Name data has already been read convert it and compare. + string localName = localEncoding.GetString(nameData); + + // Central directory and local entry name match + if (localName != entry.Name) + { + throw new ZipException("Central header and local header file name mismatch"); + } + + // Directories have zero actual size but can have compressed size + if (entry.IsDirectory) + { + if (size > 0) + { + throw new ZipException("Directory cannot have size"); + } + + // There may be other cases where the compressed size can be greater than this? + // If so until details are known we will be strict. + if (entry.IsCrypted) + { + if (compressedSize > entry.EncryptionOverheadSize + 2) + { + throw new ZipException("Directory compressed size invalid"); + } + } + else if (compressedSize > 2) + { + // When not compressed the directory size can validly be 2 bytes + // if the true size wasn't known when data was originally being written. + // NOTE: Versions of the library 0.85.4 and earlier always added 2 bytes + throw new ZipException("Directory compressed size invalid"); + } + } + + if (!ZipNameTransform.IsValidName(localName, true)) + { + throw new ZipException("Name is invalid"); + } + } + + // Tests that apply to both data and header. + + // Size can be verified only if it is known in the local header. + // it will always be known in the central header. + if (((localFlags & (int)GeneralBitFlags.Descriptor) == 0) || + ((size > 0 || compressedSize > 0) && entry.Size > 0)) + { + if ((size != 0) + && (size != entry.Size)) + { + throw new ZipException( + string.Format("Size mismatch between central header({0}) and local header({1})", + entry.Size, size)); + } + + if ((compressedSize != 0) + && (compressedSize != entry.CompressedSize && compressedSize != 0xFFFFFFFF && compressedSize != -1)) + { + throw new ZipException( + string.Format("Compressed size mismatch between central header({0}) and local header({1})", + entry.CompressedSize, compressedSize)); + } + } + + int extraLength = storedNameLength + extraDataLength; + return offsetOfFirstEntry + entry.Offset + ZipConstants.LocalHeaderBaseSize + extraLength; + } + } + + #endregion Archive Testing + + #region Updating + + private const int DefaultBufferSize = 4096; + + /// + /// The kind of update to apply. + /// + private enum UpdateCommand + { + Copy, // Copy original file contents. + Modify, // Change encryption, compression, attributes, name, time etc, of an existing file. + Add, // Add a new file to the archive. + } + + #region Properties + + /// + /// Get / set the to apply to names when updating. + /// + public INameTransform NameTransform + { + get + { + return updateEntryFactory_.NameTransform; + } + + set + { + updateEntryFactory_.NameTransform = value; + } + } + + /// + /// Get/set the used to generate values + /// during updates. + /// + public IEntryFactory EntryFactory + { + get + { + return updateEntryFactory_; + } + + set + { + if (value == null) + { + updateEntryFactory_ = new ZipEntryFactory(); + } + else + { + updateEntryFactory_ = value; + } + } + } + + /// + /// Get /set the buffer size to be used when updating this zip file. + /// + public int BufferSize + { + get { return bufferSize_; } + set + { + if (value < 1024) + { + throw new ArgumentOutOfRangeException(nameof(value), "cannot be below 1024"); + } + + if (bufferSize_ != value) + { + bufferSize_ = value; + copyBuffer_ = null; + } + } + } + + /// + /// Get a value indicating an update has been started. + /// + public bool IsUpdating + { + get { return updates_ != null; } + } + + /// + /// Get / set a value indicating how Zip64 Extension usage is determined when adding entries. + /// + public UseZip64 UseZip64 + { + get { return useZip64_; } + set { useZip64_ = value; } + } + + #endregion Properties + + #region Immediate updating + + // TBD: Direct form of updating + // + // public void Update(IEntryMatcher deleteMatcher) + // { + // } + // + // public void Update(IScanner addScanner) + // { + // } + + #endregion Immediate updating + + #region Deferred Updating + + /// + /// Begin updating this archive. + /// + /// The archive storage for use during the update. + /// The data source to utilise during updating. + /// ZipFile has been closed. + /// One of the arguments provided is null + /// ZipFile has been closed. + public void BeginUpdate(IArchiveStorage archiveStorage, IDynamicDataSource dataSource) + { + if (isDisposed_) + { + throw new ObjectDisposedException("ZipFile"); + } + + if (IsEmbeddedArchive) + { + throw new ZipException("Cannot update embedded/SFX archives"); + } + + archiveStorage_ = archiveStorage ?? throw new ArgumentNullException(nameof(archiveStorage)); + updateDataSource_ = dataSource ?? throw new ArgumentNullException(nameof(dataSource)); + + // NOTE: the baseStream_ may not currently support writing or seeking. + + updateIndex_ = new Dictionary(); + + updates_ = new List(entries_.Length); + foreach (ZipEntry entry in entries_) + { + int index = updates_.Count; + updates_.Add(new ZipUpdate(entry)); + updateIndex_.Add(entry.Name, index); + } + + // We must sort by offset before using offset's calculated sizes + updates_.Sort(new UpdateComparer()); + + int idx = 0; + foreach (ZipUpdate update in updates_) + { + //If last entry, there is no next entry offset to use + if (idx == updates_.Count - 1) + break; + + update.OffsetBasedSize = ((ZipUpdate)updates_[idx + 1]).Entry.Offset - update.Entry.Offset; + idx++; + } + updateCount_ = updates_.Count; + + contentsEdited_ = false; + commentEdited_ = false; + newComment_ = null; + } + + /// + /// Begin updating to this archive. + /// + /// The storage to use during the update. + public void BeginUpdate(IArchiveStorage archiveStorage) + { + BeginUpdate(archiveStorage, new DynamicDiskDataSource()); + } + + /// + /// Begin updating this archive. + /// + /// + /// + /// + public void BeginUpdate() + { + if (Name == null) + { + BeginUpdate(new MemoryArchiveStorage(), new DynamicDiskDataSource()); + } + else + { + BeginUpdate(new DiskArchiveStorage(this), new DynamicDiskDataSource()); + } + } + + /// + /// Commit current updates, updating this archive. + /// + /// + /// + /// ZipFile has been closed. + public void CommitUpdate() + { + if (isDisposed_) + { + throw new ObjectDisposedException("ZipFile"); + } + + CheckUpdating(); + + try + { + updateIndex_.Clear(); + updateIndex_ = null; + + if (contentsEdited_) + { + RunUpdates(); + } + else if (commentEdited_) + { + UpdateCommentOnly(); + } + else + { + // Create an empty archive if none existed originally. + if (entries_.Length != 0) return; + byte[] theComment = (newComment_ != null) + ? newComment_.RawComment + : _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment_); + ZipFormat.WriteEndOfCentralDirectory(baseStream_, 0, 0, 0, theComment); + } + } + finally + { + PostUpdateCleanup(); + } + } + + /// + /// Abort updating leaving the archive unchanged. + /// + /// + /// + public void AbortUpdate() + { + PostUpdateCleanup(); + } + + /// + /// Set the file comment to be recorded when the current update is commited. + /// + /// The comment to record. + /// ZipFile has been closed. + public void SetComment(string comment) + { + if (isDisposed_) + { + throw new ObjectDisposedException("ZipFile"); + } + + CheckUpdating(); + + newComment_ = new ZipString(comment, _stringCodec.ZipArchiveCommentEncoding); + + if (newComment_.RawLength > 0xffff) + { + newComment_ = null; + throw new ZipException("Comment length exceeds maximum - 65535"); + } + + // We dont take account of the original and current comment appearing to be the same + // as encoding may be different. + commentEdited_ = true; + } + + #endregion Deferred Updating + + #region Adding Entries + + private void AddUpdate(ZipUpdate update) + { + contentsEdited_ = true; + + int index = FindExistingUpdate(update.Entry.Name, isEntryName: true); + + if (index >= 0) + { + if (updates_[index] == null) + { + updateCount_ += 1; + } + + // Direct replacement is faster than delete and add. + updates_[index] = update; + } + else + { + index = updates_.Count; + updates_.Add(update); + updateCount_ += 1; + updateIndex_.Add(update.Entry.Name, index); + } + } + + /// + /// Add a new entry to the archive. + /// + /// The name of the file to add. + /// The compression method to use. + /// Ensure Unicode text is used for name and comment for this entry. + /// Argument supplied is null. + /// ZipFile has been closed. + /// Compression method is not supported for creating entries. + public void Add(string fileName, CompressionMethod compressionMethod, bool useUnicodeText) + { + if (fileName == null) + { + throw new ArgumentNullException(nameof(fileName)); + } + + if (isDisposed_) + { + throw new ObjectDisposedException("ZipFile"); + } + + CheckSupportedCompressionMethod(compressionMethod); + CheckUpdating(); + contentsEdited_ = true; + + ZipEntry entry = EntryFactory.MakeFileEntry(fileName); + entry.IsUnicodeText = useUnicodeText; + entry.CompressionMethod = compressionMethod; + + AddUpdate(new ZipUpdate(fileName, entry)); + } + + /// + /// Add a new entry to the archive. + /// + /// The name of the file to add. + /// The compression method to use. + /// ZipFile has been closed. + /// Compression method is not supported for creating entries. + public void Add(string fileName, CompressionMethod compressionMethod) + { + if (fileName == null) + { + throw new ArgumentNullException(nameof(fileName)); + } + + CheckSupportedCompressionMethod(compressionMethod); + CheckUpdating(); + contentsEdited_ = true; + + ZipEntry entry = EntryFactory.MakeFileEntry(fileName); + entry.CompressionMethod = compressionMethod; + AddUpdate(new ZipUpdate(fileName, entry)); + } + + /// + /// Add a file to the archive. + /// + /// The name of the file to add. + /// Argument supplied is null. + public void Add(string fileName) + { + if (fileName == null) + { + throw new ArgumentNullException(nameof(fileName)); + } + + CheckUpdating(); + AddUpdate(new ZipUpdate(fileName, EntryFactory.MakeFileEntry(fileName))); + } + + /// + /// Add a file to the archive. + /// + /// The name of the file to add. + /// The name to use for the on the Zip file created. + /// Argument supplied is null. + public void Add(string fileName, string entryName) + { + if (fileName == null) + { + throw new ArgumentNullException(nameof(fileName)); + } + + if (entryName == null) + { + throw new ArgumentNullException(nameof(entryName)); + } + + CheckUpdating(); + AddUpdate(new ZipUpdate(fileName, EntryFactory.MakeFileEntry(fileName, entryName, true))); + } + + /// + /// Add a file entry with data. + /// + /// The source of the data for this entry. + /// The name to give to the entry. + public void Add(IStaticDataSource dataSource, string entryName) + { + if (dataSource == null) + { + throw new ArgumentNullException(nameof(dataSource)); + } + + if (entryName == null) + { + throw new ArgumentNullException(nameof(entryName)); + } + + CheckUpdating(); + AddUpdate(new ZipUpdate(dataSource, EntryFactory.MakeFileEntry(entryName, false))); + } + + /// + /// Add a file entry with data. + /// + /// The source of the data for this entry. + /// The name to give to the entry. + /// The compression method to use. + /// Compression method is not supported for creating entries. + public void Add(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod) + { + if (dataSource == null) + { + throw new ArgumentNullException(nameof(dataSource)); + } + + if (entryName == null) + { + throw new ArgumentNullException(nameof(entryName)); + } + + CheckSupportedCompressionMethod(compressionMethod); + CheckUpdating(); + + ZipEntry entry = EntryFactory.MakeFileEntry(entryName, false); + entry.CompressionMethod = compressionMethod; + + AddUpdate(new ZipUpdate(dataSource, entry)); + } + + /// + /// Add a file entry with data. + /// + /// The source of the data for this entry. + /// The name to give to the entry. + /// The compression method to use. + /// Ensure Unicode text is used for name and comments for this entry. + /// Compression method is not supported for creating entries. + public void Add(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod, bool useUnicodeText) + { + if (dataSource == null) + { + throw new ArgumentNullException(nameof(dataSource)); + } + + if (entryName == null) + { + throw new ArgumentNullException(nameof(entryName)); + } + + CheckSupportedCompressionMethod(compressionMethod); + CheckUpdating(); + + ZipEntry entry = EntryFactory.MakeFileEntry(entryName, false); + entry.IsUnicodeText = useUnicodeText; + entry.CompressionMethod = compressionMethod; + + AddUpdate(new ZipUpdate(dataSource, entry)); + } + + /// + /// Add a that contains no data. + /// + /// The entry to add. + /// This can be used to add directories, volume labels, or empty file entries. + public void Add(ZipEntry entry) + { + if (entry == null) + { + throw new ArgumentNullException(nameof(entry)); + } + + CheckUpdating(); + + if ((entry.Size != 0) || (entry.CompressedSize != 0)) + { + throw new ZipException("Entry cannot have any data"); + } + + AddUpdate(new ZipUpdate(UpdateCommand.Add, entry)); + } + + /// + /// Add a with data. + /// + /// The source of the data for this entry. + /// The entry to add. + /// This can be used to add file entries with a custom data source. + /// + /// The encryption method specified in is unsupported. + /// + /// Compression method is not supported for creating entries. + public void Add(IStaticDataSource dataSource, ZipEntry entry) + { + if (entry == null) + { + throw new ArgumentNullException(nameof(entry)); + } + + if (dataSource == null) + { + throw new ArgumentNullException(nameof(dataSource)); + } + + // We don't currently support adding entries with AES encryption, so throw + // up front instead of failing or falling back to ZipCrypto later on + if (entry.AESKeySize > 0) + { + throw new NotSupportedException("Creation of AES encrypted entries is not supported"); + } + + CheckSupportedCompressionMethod(entry.CompressionMethod); + CheckUpdating(); + + AddUpdate(new ZipUpdate(dataSource, entry)); + } + + /// + /// Add a directory entry to the archive. + /// + /// The directory to add. + public void AddDirectory(string directoryName) + { + if (directoryName == null) + { + throw new ArgumentNullException(nameof(directoryName)); + } + + CheckUpdating(); + + ZipEntry dirEntry = EntryFactory.MakeDirectoryEntry(directoryName); + AddUpdate(new ZipUpdate(UpdateCommand.Add, dirEntry)); + } + + /// + /// Check if the specified compression method is supported for adding a new entry. + /// + /// The compression method for the new entry. + private static void CheckSupportedCompressionMethod(CompressionMethod compressionMethod) + { + if (compressionMethod != CompressionMethod.Deflated && compressionMethod != CompressionMethod.Stored && compressionMethod != CompressionMethod.BZip2) + { + throw new NotImplementedException("Compression method not supported"); + } + } + + #endregion Adding Entries + + #region Modifying Entries + + /* Modify not yet ready for public consumption. + Direct modification of an entry should not overwrite original data before its read. + Safe mode is trivial in this sense. + public void Modify(ZipEntry original, ZipEntry updated) + { + if ( original == null ) { + throw new ArgumentNullException("original"); + } + if ( updated == null ) { + throw new ArgumentNullException("updated"); + } + CheckUpdating(); + contentsEdited_ = true; + updates_.Add(new ZipUpdate(original, updated)); + } + */ + + #endregion Modifying Entries + + #region Deleting Entries + + /// + /// Delete an entry by name + /// + /// The filename to delete + /// True if the entry was found and deleted; false otherwise. + public bool Delete(string fileName) + { + if (fileName == null) + { + throw new ArgumentNullException(nameof(fileName)); + } + + CheckUpdating(); + + bool result = false; + int index = FindExistingUpdate(fileName); + if ((index >= 0) && (updates_[index] != null)) + { + result = true; + contentsEdited_ = true; + updates_[index] = null; + updateCount_ -= 1; + } + else + { + throw new ZipException("Cannot find entry to delete"); + } + return result; + } + + /// + /// Delete a from the archive. + /// + /// The entry to delete. + public void Delete(ZipEntry entry) + { + if (entry == null) + { + throw new ArgumentNullException(nameof(entry)); + } + + CheckUpdating(); + + int index = FindExistingUpdate(entry); + if (index >= 0) + { + contentsEdited_ = true; + updates_[index] = null; + updateCount_ -= 1; + } + else + { + throw new ZipException("Cannot find entry to delete"); + } + } + + #endregion Deleting Entries + + #region Update Support + + #region Writing Values/Headers + + private void WriteLEShort(int value) + { + baseStream_.WriteByte((byte)(value & 0xff)); + baseStream_.WriteByte((byte)((value >> 8) & 0xff)); + } + + /// + /// Write an unsigned short in little endian byte order. + /// + private void WriteLEUshort(ushort value) + { + baseStream_.WriteByte((byte)(value & 0xff)); + baseStream_.WriteByte((byte)(value >> 8)); + } + + /// + /// Write an int in little endian byte order. + /// + private void WriteLEInt(int value) + { + WriteLEShort(value & 0xffff); + WriteLEShort(value >> 16); + } + + /// + /// Write an unsigned int in little endian byte order. + /// + private void WriteLEUint(uint value) + { + WriteLEUshort((ushort)(value & 0xffff)); + WriteLEUshort((ushort)(value >> 16)); + } + + /// + /// Write a long in little endian byte order. + /// + private void WriteLeLong(long value) + { + WriteLEInt((int)(value & 0xffffffff)); + WriteLEInt((int)(value >> 32)); + } + + private void WriteLEUlong(ulong value) + { + WriteLEUint((uint)(value & 0xffffffff)); + WriteLEUint((uint)(value >> 32)); + } + + private void WriteLocalEntryHeader(ZipUpdate update) + { + ZipEntry entry = update.OutEntry; + + // TODO: Local offset will require adjusting for multi-disk zip files. + entry.Offset = baseStream_.Position; + + // TODO: Need to clear any entry flags that dont make sense or throw an exception here. + if (update.Command != UpdateCommand.Copy) + { + if (entry.CompressionMethod == CompressionMethod.Deflated) + { + if (entry.Size == 0) + { + // No need to compress - no data. + entry.CompressedSize = entry.Size; + entry.Crc = 0; + entry.CompressionMethod = CompressionMethod.Stored; + } + } + else if (entry.CompressionMethod == CompressionMethod.Stored) + { + entry.Flags &= ~(int)GeneralBitFlags.Descriptor; + } + + if (HaveKeys) + { + entry.IsCrypted = true; + if (entry.Crc < 0) + { + entry.Flags |= (int)GeneralBitFlags.Descriptor; + } + } + else + { + entry.IsCrypted = false; + } + + switch (useZip64_) + { + case UseZip64.Dynamic: + if (entry.Size < 0) + { + entry.ForceZip64(); + } + break; + + case UseZip64.On: + entry.ForceZip64(); + break; + + case UseZip64.Off: + // Do nothing. The entry itself may be using Zip64 independently. + break; + } + } + + // Write the local file header + WriteLEInt(ZipConstants.LocalHeaderSignature); + + WriteLEShort(entry.Version); + WriteLEShort(entry.Flags); + + WriteLEShort((byte)entry.CompressionMethodForHeader); + WriteLEInt((int)entry.DosTime); + + if (!entry.HasCrc) + { + // Note patch address for updating CRC later. + update.CrcPatchOffset = baseStream_.Position; + WriteLEInt((int)0); + } + else + { + WriteLEInt(unchecked((int)entry.Crc)); + } + + if (entry.LocalHeaderRequiresZip64) + { + WriteLEInt(-1); + WriteLEInt(-1); + } + else + { + if ((entry.CompressedSize < 0) || (entry.Size < 0)) + { + update.SizePatchOffset = baseStream_.Position; + } + + WriteLEInt((int)entry.CompressedSize); + WriteLEInt((int)entry.Size); + } + + var entryEncoding = _stringCodec.ZipInputEncoding(entry.Flags); + byte[] name = entryEncoding.GetBytes(entry.Name); + + if (name.Length > 0xFFFF) + { + throw new ZipException("Entry name too long."); + } + + var ed = new ZipExtraData(entry.ExtraData); + + if (entry.LocalHeaderRequiresZip64) + { + ed.StartNewEntry(); + + // Local entry header always includes size and compressed size. + // NOTE the order of these fields is reversed when compared to the normal headers! + ed.AddLeLong(entry.Size); + ed.AddLeLong(entry.CompressedSize); + ed.AddNewEntry(1); + } + else + { + ed.Delete(1); + } + + entry.ExtraData = ed.GetEntryData(); + + WriteLEShort(name.Length); + WriteLEShort(entry.ExtraData.Length); + + if (name.Length > 0) + { + baseStream_.Write(name, 0, name.Length); + } + + if (entry.LocalHeaderRequiresZip64) + { + if (!ed.Find(1)) + { + throw new ZipException("Internal error cannot find extra data"); + } + + update.SizePatchOffset = baseStream_.Position + ed.CurrentReadIndex; + } + + if (entry.ExtraData.Length > 0) + { + baseStream_.Write(entry.ExtraData, 0, entry.ExtraData.Length); + } + } + + private int WriteCentralDirectoryHeader(ZipEntry entry) + { + if (entry.CompressedSize < 0) + { + throw new ZipException("Attempt to write central directory entry with unknown csize"); + } + + if (entry.Size < 0) + { + throw new ZipException("Attempt to write central directory entry with unknown size"); + } + + if (entry.Crc < 0) + { + throw new ZipException("Attempt to write central directory entry with unknown crc"); + } + + // Write the central file header + WriteLEInt(ZipConstants.CentralHeaderSignature); + + // Version made by + WriteLEShort((entry.HostSystem << 8) | entry.VersionMadeBy); + + // Version required to extract + WriteLEShort(entry.Version); + + WriteLEShort(entry.Flags); + + unchecked + { + WriteLEShort((byte)entry.CompressionMethodForHeader); + WriteLEInt((int)entry.DosTime); + WriteLEInt((int)entry.Crc); + } + + bool useExtraCompressedSize = false; //Do we want to store the compressed size in the extra data? + if ((entry.IsZip64Forced()) || (entry.CompressedSize >= 0xffffffff)) + { + useExtraCompressedSize = true; + WriteLEInt(-1); + } + else + { + WriteLEInt((int)(entry.CompressedSize & 0xffffffff)); + } + + bool useExtraUncompressedSize = false; //Do we want to store the uncompressed size in the extra data? + if ((entry.IsZip64Forced()) || (entry.Size >= 0xffffffff)) + { + useExtraUncompressedSize = true; + WriteLEInt(-1); + } + else + { + WriteLEInt((int)entry.Size); + } + + var entryEncoding = _stringCodec.ZipInputEncoding(entry.Flags); + byte[] name = entryEncoding.GetBytes(entry.Name); + + if (name.Length > 0xFFFF) + { + throw new ZipException("Entry name is too long."); + } + + WriteLEShort(name.Length); + + // Central header extra data is different to local header version so regenerate. + var ed = new ZipExtraData(entry.ExtraData); + + if (entry.CentralHeaderRequiresZip64) + { + ed.StartNewEntry(); + + if (useExtraUncompressedSize) + { + ed.AddLeLong(entry.Size); + } + + if (useExtraCompressedSize) + { + ed.AddLeLong(entry.CompressedSize); + } + + if (entry.Offset >= 0xffffffff) + { + ed.AddLeLong(entry.Offset); + } + + // Number of disk on which this file starts isnt supported and is never written here. + ed.AddNewEntry(1); + } + else + { + // Should have already be done when local header was added. + ed.Delete(1); + } + + byte[] centralExtraData = ed.GetEntryData(); + + WriteLEShort(centralExtraData.Length); + WriteLEShort(entry.Comment != null ? entry.Comment.Length : 0); + + WriteLEShort(0); // disk number + WriteLEShort(0); // internal file attributes + + // External file attributes... + if (entry.ExternalFileAttributes != -1) + { + WriteLEInt(entry.ExternalFileAttributes); + } + else + { + if (entry.IsDirectory) + { + WriteLEUint(16); + } + else + { + WriteLEUint(0); + } + } + + if (entry.Offset >= 0xffffffff) + { + WriteLEUint(0xffffffff); + } + else + { + WriteLEUint((uint)(int)entry.Offset); + } + + if (name.Length > 0) + { + baseStream_.Write(name, 0, name.Length); + } + + if (centralExtraData.Length > 0) + { + baseStream_.Write(centralExtraData, 0, centralExtraData.Length); + } + + byte[] rawComment = (entry.Comment != null) ? Encoding.ASCII.GetBytes(entry.Comment) : Empty.Array(); + + if (rawComment.Length > 0) + { + baseStream_.Write(rawComment, 0, rawComment.Length); + } + + return ZipConstants.CentralHeaderBaseSize + name.Length + centralExtraData.Length + rawComment.Length; + } + + #endregion Writing Values/Headers + + private void PostUpdateCleanup() + { + updateDataSource_ = null; + updates_ = null; + updateIndex_ = null; + + if (archiveStorage_ != null) + { + archiveStorage_.Dispose(); + archiveStorage_ = null; + } + } + + private string GetTransformedFileName(string name) + { + INameTransform transform = NameTransform; + return (transform != null) ? + transform.TransformFile(name) : + name; + } + + private string GetTransformedDirectoryName(string name) + { + INameTransform transform = NameTransform; + return (transform != null) ? + transform.TransformDirectory(name) : + name; + } + + /// + /// Get a raw memory buffer. + /// + /// Returns a raw memory buffer. + private byte[] GetBuffer() + { + if (copyBuffer_ == null) + { + copyBuffer_ = new byte[bufferSize_]; + } + return copyBuffer_; + } + + private void CopyDescriptorBytes(ZipUpdate update, Stream dest, Stream source) + { + // Don't include the signature size to allow copy without seeking + var bytesToCopy = GetDescriptorSize(update, false); + + // Don't touch the source stream if no descriptor is present + if (bytesToCopy == 0) return; + + var buffer = GetBuffer(); + + // Copy the first 4 bytes of the descriptor + source.Read(buffer, 0, sizeof(int)); + dest.Write(buffer, 0, sizeof(int)); + + if (BitConverter.ToUInt32(buffer, 0) != ZipConstants.DataDescriptorSignature) + { + // The initial bytes wasn't the descriptor, reduce the pending byte count + bytesToCopy -= buffer.Length; + } + + while (bytesToCopy > 0) + { + int readSize = Math.Min(buffer.Length, bytesToCopy); + + int bytesRead = source.Read(buffer, 0, readSize); + if (bytesRead > 0) + { + dest.Write(buffer, 0, bytesRead); + bytesToCopy -= bytesRead; + } + else + { + throw new ZipException("Unxpected end of stream"); + } + } + } + + private void CopyBytes(ZipUpdate update, Stream destination, Stream source, + long bytesToCopy, bool updateCrc) + { + if (destination == source) + { + throw new InvalidOperationException("Destination and source are the same"); + } + + // NOTE: Compressed size is updated elsewhere. + var crc = new Crc32(); + byte[] buffer = GetBuffer(); + + long targetBytes = bytesToCopy; + long totalBytesRead = 0; + + int bytesRead; + do + { + int readSize = buffer.Length; + + if (bytesToCopy < readSize) + { + readSize = (int)bytesToCopy; + } + + bytesRead = source.Read(buffer, 0, readSize); + if (bytesRead > 0) + { + if (updateCrc) + { + crc.Update(new ArraySegment(buffer, 0, bytesRead)); + } + destination.Write(buffer, 0, bytesRead); + bytesToCopy -= bytesRead; + totalBytesRead += bytesRead; + } + } + while ((bytesRead > 0) && (bytesToCopy > 0)); + + if (totalBytesRead != targetBytes) + { + throw new ZipException(string.Format("Failed to copy bytes expected {0} read {1}", targetBytes, totalBytesRead)); + } + + if (updateCrc) + { + update.OutEntry.Crc = crc.Value; + } + } + + /// + /// Get the size of the source descriptor for a . + /// + /// The update to get the size for. + /// Whether to include the signature size + /// The descriptor size, zero if there isn't one. + private static int GetDescriptorSize(ZipUpdate update, bool includingSignature) + { + if (!((GeneralBitFlags)update.Entry.Flags).HasFlag(GeneralBitFlags.Descriptor)) + return 0; + + var descriptorWithSignature = update.Entry.LocalHeaderRequiresZip64 + ? ZipConstants.Zip64DataDescriptorSize + : ZipConstants.DataDescriptorSize; + + return includingSignature + ? descriptorWithSignature + : descriptorWithSignature - sizeof(int); + } + + private void CopyDescriptorBytesDirect(ZipUpdate update, Stream stream, ref long destinationPosition, long sourcePosition) + { + var buffer = GetBuffer(); ; + + stream.Position = sourcePosition; + stream.Read(buffer, 0, sizeof(int)); + var sourceHasSignature = BitConverter.ToUInt32(buffer, 0) == ZipConstants.DataDescriptorSignature; + + var bytesToCopy = GetDescriptorSize(update, sourceHasSignature); + + while (bytesToCopy > 0) + { + stream.Position = sourcePosition; + + var bytesRead = stream.Read(buffer, 0, bytesToCopy); + if (bytesRead > 0) + { + stream.Position = destinationPosition; + stream.Write(buffer, 0, bytesRead); + bytesToCopy -= bytesRead; + destinationPosition += bytesRead; + sourcePosition += bytesRead; + } + else + { + throw new ZipException("Unexpected end of stream"); + } + } + } + + private void CopyEntryDataDirect(ZipUpdate update, Stream stream, bool updateCrc, ref long destinationPosition, ref long sourcePosition) + { + long bytesToCopy = update.Entry.CompressedSize; + + // NOTE: Compressed size is updated elsewhere. + var crc = new Crc32(); + byte[] buffer = GetBuffer(); + + long targetBytes = bytesToCopy; + long totalBytesRead = 0; + + int bytesRead; + do + { + int readSize = buffer.Length; + + if (bytesToCopy < readSize) + { + readSize = (int)bytesToCopy; + } + + stream.Position = sourcePosition; + bytesRead = stream.Read(buffer, 0, readSize); + if (bytesRead > 0) + { + if (updateCrc) + { + crc.Update(new ArraySegment(buffer, 0, bytesRead)); + } + stream.Position = destinationPosition; + stream.Write(buffer, 0, bytesRead); + + destinationPosition += bytesRead; + sourcePosition += bytesRead; + bytesToCopy -= bytesRead; + totalBytesRead += bytesRead; + } + } + while ((bytesRead > 0) && (bytesToCopy > 0)); + + if (totalBytesRead != targetBytes) + { + throw new ZipException(string.Format("Failed to copy bytes expected {0} read {1}", targetBytes, totalBytesRead)); + } + + if (updateCrc) + { + update.OutEntry.Crc = crc.Value; + } + } + + private int FindExistingUpdate(ZipEntry entry) + { + int result = -1; + if (updateIndex_.ContainsKey(entry.Name)) + { + result = (int)updateIndex_[entry.Name]; + } + /* + // This is slow like the coming of the next ice age but takes less storage and may be useful + // for CF? + for (int index = 0; index < updates_.Count; ++index) + { + ZipUpdate zu = ( ZipUpdate )updates_[index]; + if ( (zu.Entry.ZipFileIndex == entry.ZipFileIndex) && + (string.Compare(convertedName, zu.Entry.Name, true, CultureInfo.InvariantCulture) == 0) ) { + result = index; + break; + } + } + */ + return result; + } + + private int FindExistingUpdate(string fileName, bool isEntryName = false) + { + int result = -1; + + string convertedName = !isEntryName ? GetTransformedFileName(fileName) : fileName; + + if (updateIndex_.ContainsKey(convertedName)) + { + result = (int)updateIndex_[convertedName]; + } + + /* + // This is slow like the coming of the next ice age but takes less storage and may be useful + // for CF? + for ( int index = 0; index < updates_.Count; ++index ) { + if ( string.Compare(convertedName, (( ZipUpdate )updates_[index]).Entry.Name, + true, CultureInfo.InvariantCulture) == 0 ) { + result = index; + break; + } + } + */ + + return result; + } + + /// + /// Get an output stream for the specified + /// + /// The entry to get an output stream for. + /// The output stream obtained for the entry. + private Stream GetOutputStream(ZipEntry entry) + { + Stream result = baseStream_; + + if (entry.IsCrypted == true) + { + result = CreateAndInitEncryptionStream(result, entry); + } + + switch (entry.CompressionMethod) + { + case CompressionMethod.Stored: + if (!entry.IsCrypted) + { + // If there is an encryption stream in use, that can be returned directly + // otherwise, wrap the base stream in an UncompressedStream instead of returning it directly + result = new UncompressedStream(result); + } + break; + + case CompressionMethod.Deflated: + var dos = new DeflaterOutputStream(result, new Deflater(9, true)) + { + // If there is an encryption stream in use, then we want that to be disposed when the deflator stream is disposed + // If not, then we don't want it to dispose the base stream + IsStreamOwner = entry.IsCrypted + }; + result = dos; + break; + + case CompressionMethod.BZip2: + var bzos = new BZip2.BZip2OutputStream(result) + { + // If there is an encryption stream in use, then we want that to be disposed when the BZip2OutputStream stream is disposed + // If not, then we don't want it to dispose the base stream + IsStreamOwner = entry.IsCrypted + }; + result = bzos; + break; + + default: + throw new ZipException("Unknown compression method " + entry.CompressionMethod); + } + return result; + } + + private void AddEntry(ZipFile workFile, ZipUpdate update) + { + Stream source = null; + + if (update.Entry.IsFile) + { + source = update.GetSource(); + + if (source == null) + { + source = updateDataSource_.GetSource(update.Entry, update.Filename); + } + } + + var useCrc = update.Entry.AESKeySize == 0; + + if (source != null) + { + using (source) + { + long sourceStreamLength = source.Length; + if (update.OutEntry.Size < 0) + { + update.OutEntry.Size = sourceStreamLength; + } + else + { + // Check for errant entries. + if (update.OutEntry.Size != sourceStreamLength) + { + throw new ZipException("Entry size/stream size mismatch"); + } + } + + workFile.WriteLocalEntryHeader(update); + + long dataStart = workFile.baseStream_.Position; + + using (Stream output = workFile.GetOutputStream(update.OutEntry)) + { + CopyBytes(update, output, source, sourceStreamLength, useCrc); + } + + long dataEnd = workFile.baseStream_.Position; + update.OutEntry.CompressedSize = dataEnd - dataStart; + + if ((update.OutEntry.Flags & (int)GeneralBitFlags.Descriptor) == (int)GeneralBitFlags.Descriptor) + { + ZipFormat.WriteDataDescriptor(workFile.baseStream_, update.OutEntry); + } + } + } + else + { + workFile.WriteLocalEntryHeader(update); + update.OutEntry.CompressedSize = 0; + } + } + + private void ModifyEntry(ZipFile workFile, ZipUpdate update) + { + workFile.WriteLocalEntryHeader(update); + long dataStart = workFile.baseStream_.Position; + + // TODO: This is slow if the changes don't effect the data!! + if (update.Entry.IsFile && (update.Filename != null)) + { + using (Stream output = workFile.GetOutputStream(update.OutEntry)) + { + using (Stream source = this.GetInputStream(update.Entry)) + { + CopyBytes(update, output, source, source.Length, true); + } + } + } + + long dataEnd = workFile.baseStream_.Position; + update.Entry.CompressedSize = dataEnd - dataStart; + } + + private void CopyEntryDirect(ZipFile workFile, ZipUpdate update, ref long destinationPosition) + { + bool skipOver = false || update.Entry.Offset == destinationPosition; + + if (!skipOver) + { + baseStream_.Position = destinationPosition; + workFile.WriteLocalEntryHeader(update); + destinationPosition = baseStream_.Position; + } + + long sourcePosition = 0; + + const int NameLengthOffset = 26; + + // TODO: Add base for SFX friendly handling + long entryDataOffset = update.Entry.Offset + NameLengthOffset; + + baseStream_.Seek(entryDataOffset, SeekOrigin.Begin); + + // Clumsy way of handling retrieving the original name and extra data length for now. + // TODO: Stop re-reading name and data length in CopyEntryDirect. + + uint nameLength = ReadLEUshort(); + uint extraLength = ReadLEUshort(); + + sourcePosition = baseStream_.Position + nameLength + extraLength; + + if (skipOver) + { + if (update.OffsetBasedSize != -1) + { + destinationPosition += update.OffsetBasedSize; + } + else + { + // Skip entry header + destinationPosition += (sourcePosition - entryDataOffset) + NameLengthOffset; + + // Skip entry compressed data + destinationPosition += update.Entry.CompressedSize; + + // Seek to end of entry to check for descriptor signature + baseStream_.Seek(destinationPosition, SeekOrigin.Begin); + + var descriptorHasSignature = ReadLEUint() == ZipConstants.DataDescriptorSignature; + + // Skip descriptor and it's signature (if present) + destinationPosition += GetDescriptorSize(update, descriptorHasSignature); + } + } + else + { + if (update.Entry.CompressedSize > 0) + { + CopyEntryDataDirect(update, baseStream_, false, ref destinationPosition, ref sourcePosition); + } + CopyDescriptorBytesDirect(update, baseStream_, ref destinationPosition, sourcePosition); + } + } + + private void CopyEntry(ZipFile workFile, ZipUpdate update) + { + workFile.WriteLocalEntryHeader(update); + + if (update.Entry.CompressedSize > 0) + { + const int NameLengthOffset = 26; + + long entryDataOffset = update.Entry.Offset + NameLengthOffset; + + // TODO: This wont work for SFX files! + baseStream_.Seek(entryDataOffset, SeekOrigin.Begin); + + uint nameLength = ReadLEUshort(); + uint extraLength = ReadLEUshort(); + + baseStream_.Seek(nameLength + extraLength, SeekOrigin.Current); + + CopyBytes(update, workFile.baseStream_, baseStream_, update.Entry.CompressedSize, false); + } + CopyDescriptorBytes(update, workFile.baseStream_, baseStream_); + } + + private void Reopen(Stream source) + { + isNewArchive_ = false; + baseStream_ = source ?? throw new ZipException("Failed to reopen archive - no source"); + ReadEntries(); + } + + private void Reopen() + { + if (Name == null) + { + throw new InvalidOperationException("Name is not known cannot Reopen"); + } + + Reopen(File.Open(Name, FileMode.Open, FileAccess.Read, FileShare.Read)); + } + + private void UpdateCommentOnly() + { + long baseLength = baseStream_.Length; + + Stream updateFile; + + if (archiveStorage_.UpdateMode == FileUpdateMode.Safe) + { + updateFile = archiveStorage_.MakeTemporaryCopy(baseStream_); + + baseStream_.Dispose(); + baseStream_ = null; + } + else + { + if (archiveStorage_.UpdateMode == FileUpdateMode.Direct) + { + // TODO: archiveStorage wasnt originally intended for this use. + // Need to revisit this to tidy up handling as archive storage currently doesnt + // handle the original stream well. + // The problem is when using an existing zip archive with an in memory archive storage. + // The open stream wont support writing but the memory storage should open the same file not an in memory one. + + // Need to tidy up the archive storage interface and contract basically. + baseStream_ = archiveStorage_.OpenForDirectUpdate(baseStream_); + updateFile = baseStream_; + } + else + { + baseStream_.Dispose(); + baseStream_ = null; + updateFile = new FileStream(Name, FileMode.Open, FileAccess.ReadWrite); + } + } + + try + { + long locatedCentralDirOffset = + ZipFormat.LocateBlockWithSignature(updateFile, ZipConstants.EndOfCentralDirectorySignature, + baseLength, ZipConstants.EndOfCentralRecordBaseSize, 0xffff); + if (locatedCentralDirOffset < 0) + { + throw new ZipException("Cannot find central directory"); + } + + const int CentralHeaderCommentSizeOffset = 16; + updateFile.Position += CentralHeaderCommentSizeOffset; + + byte[] rawComment = newComment_.RawComment; + + updateFile.WriteLEShort(rawComment.Length); + updateFile.Write(rawComment, 0, rawComment.Length); + updateFile.SetLength(updateFile.Position); + } + finally + { + if (updateFile != baseStream_) + updateFile.Dispose(); + } + + if (archiveStorage_.UpdateMode == FileUpdateMode.Safe) + { + Reopen(archiveStorage_.ConvertTemporaryToFinal()); + } + else + { + ReadEntries(); + } + } + + /// + /// Class used to sort updates. + /// + private class UpdateComparer : IComparer + { + /// + /// Compares two objects and returns a value indicating whether one is + /// less than, equal to or greater than the other. + /// + /// First object to compare + /// Second object to compare. + /// Compare result. + public int Compare(ZipUpdate x, ZipUpdate y) + { + int result; + + if (x == null) + { + if (y == null) + { + result = 0; + } + else + { + result = -1; + } + } + else if (y == null) + { + result = 1; + } + else + { + int xCmdValue = ((x.Command == UpdateCommand.Copy) || (x.Command == UpdateCommand.Modify)) ? 0 : 1; + int yCmdValue = ((y.Command == UpdateCommand.Copy) || (y.Command == UpdateCommand.Modify)) ? 0 : 1; + + result = xCmdValue - yCmdValue; + if (result == 0) + { + long offsetDiff = x.Entry.Offset - y.Entry.Offset; + if (offsetDiff < 0) + { + result = -1; + } + else if (offsetDiff == 0) + { + result = 0; + } + else + { + result = 1; + } + } + } + return result; + } + } + + private void RunUpdates() + { + long sizeEntries = 0; + long endOfStream = 0; + bool directUpdate = false; + long destinationPosition = 0; // NOT SFX friendly + + ZipFile workFile; + + if (IsNewArchive) + { + workFile = this; + workFile.baseStream_.Position = 0; + directUpdate = true; + } + else if (archiveStorage_.UpdateMode == FileUpdateMode.Direct) + { + workFile = this; + workFile.baseStream_.Position = 0; + directUpdate = true; + + // Sort the updates by offset within copies/modifies, then adds. + // This ensures that data required by copies will not be overwritten. + updates_.Sort(new UpdateComparer()); + } + else + { + workFile = ZipFile.Create(archiveStorage_.GetTemporaryOutput()); + workFile.UseZip64 = UseZip64; + + if (key != null) + { + workFile.key = (byte[])key.Clone(); + } + } + + try + { + foreach (ZipUpdate update in updates_) + { + if (update != null) + { + switch (update.Command) + { + case UpdateCommand.Copy: + if (directUpdate) + { + CopyEntryDirect(workFile, update, ref destinationPosition); + } + else + { + CopyEntry(workFile, update); + } + break; + + case UpdateCommand.Modify: + // TODO: Direct modifying of an entry will take some legwork. + ModifyEntry(workFile, update); + break; + + case UpdateCommand.Add: + if (!IsNewArchive && directUpdate) + { + workFile.baseStream_.Position = destinationPosition; + } + + AddEntry(workFile, update); + + if (directUpdate) + { + destinationPosition = workFile.baseStream_.Position; + } + break; + } + } + } + + if (!IsNewArchive && directUpdate) + { + workFile.baseStream_.Position = destinationPosition; + } + + long centralDirOffset = workFile.baseStream_.Position; + + foreach (ZipUpdate update in updates_) + { + if (update != null) + { + sizeEntries += workFile.WriteCentralDirectoryHeader(update.OutEntry); + } + } + + byte[] theComment = newComment_?.RawComment ?? _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment_); + ZipFormat.WriteEndOfCentralDirectory(workFile.baseStream_, updateCount_, + sizeEntries, centralDirOffset, theComment); + + endOfStream = workFile.baseStream_.Position; + + // And now patch entries... + foreach (ZipUpdate update in updates_) + { + if (update != null) + { + // If the size of the entry is zero leave the crc as 0 as well. + // The calculated crc will be all bits on... + if ((update.CrcPatchOffset > 0) && (update.OutEntry.CompressedSize > 0)) + { + workFile.baseStream_.Position = update.CrcPatchOffset; + workFile.WriteLEInt((int)update.OutEntry.Crc); + } + + if (update.SizePatchOffset > 0) + { + workFile.baseStream_.Position = update.SizePatchOffset; + if (update.OutEntry.LocalHeaderRequiresZip64) + { + workFile.WriteLeLong(update.OutEntry.Size); + workFile.WriteLeLong(update.OutEntry.CompressedSize); + } + else + { + workFile.WriteLEInt((int)update.OutEntry.CompressedSize); + workFile.WriteLEInt((int)update.OutEntry.Size); + } + } + } + } + } + catch + { + workFile.Close(); + if (!directUpdate && (workFile.Name != null)) + { + File.Delete(workFile.Name); + } + throw; + } + + if (directUpdate) + { + workFile.baseStream_.SetLength(endOfStream); + workFile.baseStream_.Flush(); + isNewArchive_ = false; + ReadEntries(); + } + else + { + baseStream_.Dispose(); + Reopen(archiveStorage_.ConvertTemporaryToFinal()); + } + } + + private void CheckUpdating() + { + if (updates_ == null) + { + throw new InvalidOperationException("BeginUpdate has not been called"); + } + } + + #endregion Update Support + + #region ZipUpdate class + + /// + /// Represents a pending update to a Zip file. + /// + private class ZipUpdate + { + #region Constructors + + public ZipUpdate(string fileName, ZipEntry entry) + { + command_ = UpdateCommand.Add; + entry_ = entry; + filename_ = fileName; + } + + [Obsolete] + public ZipUpdate(string fileName, string entryName, CompressionMethod compressionMethod) + { + command_ = UpdateCommand.Add; + entry_ = new ZipEntry(entryName) + { + CompressionMethod = compressionMethod + }; + filename_ = fileName; + } + + [Obsolete] + public ZipUpdate(string fileName, string entryName) + : this(fileName, entryName, CompressionMethod.Deflated) + { + // Do nothing. + } + + [Obsolete] + public ZipUpdate(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod) + { + command_ = UpdateCommand.Add; + entry_ = new ZipEntry(entryName) + { + CompressionMethod = compressionMethod + }; + dataSource_ = dataSource; + } + + public ZipUpdate(IStaticDataSource dataSource, ZipEntry entry) + { + command_ = UpdateCommand.Add; + entry_ = entry; + dataSource_ = dataSource; + } + + public ZipUpdate(ZipEntry original, ZipEntry updated) + { + throw new ZipException("Modify not currently supported"); + /* + command_ = UpdateCommand.Modify; + entry_ = ( ZipEntry )original.Clone(); + outEntry_ = ( ZipEntry )updated.Clone(); + */ + } + + public ZipUpdate(UpdateCommand command, ZipEntry entry) + { + command_ = command; + entry_ = (ZipEntry)entry.Clone(); + } + + /// + /// Copy an existing entry. + /// + /// The existing entry to copy. + public ZipUpdate(ZipEntry entry) + : this(UpdateCommand.Copy, entry) + { + // Do nothing. + } + + #endregion Constructors + + /// + /// Get the for this update. + /// + /// This is the source or original entry. + public ZipEntry Entry + { + get { return entry_; } + } + + /// + /// Get the that will be written to the updated/new file. + /// + public ZipEntry OutEntry + { + get + { + if (outEntry_ == null) + { + outEntry_ = (ZipEntry)entry_.Clone(); + } + + return outEntry_; + } + } + + /// + /// Get the command for this update. + /// + public UpdateCommand Command + { + get { return command_; } + } + + /// + /// Get the filename if any for this update. Null if none exists. + /// + public string Filename + { + get { return filename_; } + } + + /// + /// Get/set the location of the size patch for this update. + /// + public long SizePatchOffset + { + get { return sizePatchOffset_; } + set { sizePatchOffset_ = value; } + } + + /// + /// Get /set the location of the crc patch for this update. + /// + public long CrcPatchOffset + { + get { return crcPatchOffset_; } + set { crcPatchOffset_ = value; } + } + + /// + /// Get/set the size calculated by offset. + /// Specifically, the difference between this and next entry's starting offset. + /// + public long OffsetBasedSize + { + get { return _offsetBasedSize; } + set { _offsetBasedSize = value; } + } + + public Stream GetSource() + { + Stream result = null; + if (dataSource_ != null) + { + result = dataSource_.GetSource(); + } + + return result; + } + + #region Instance Fields + + private ZipEntry entry_; + private ZipEntry outEntry_; + private readonly UpdateCommand command_; + private IStaticDataSource dataSource_; + private readonly string filename_; + private long sizePatchOffset_ = -1; + private long crcPatchOffset_ = -1; + private long _offsetBasedSize = -1; + + #endregion Instance Fields + } + + #endregion ZipUpdate class + + #endregion Updating + + #region Disposing + + #region IDisposable Members + + void IDisposable.Dispose() + { + Close(); + } + + #endregion IDisposable Members + + private void DisposeInternal(bool disposing) + { + if (!isDisposed_) + { + isDisposed_ = true; + entries_ = Empty.Array(); + + if (IsStreamOwner && (baseStream_ != null)) + { + lock (baseStream_) + { + baseStream_.Dispose(); + } + } + + PostUpdateCleanup(); + } + } + + /// + /// Releases the unmanaged resources used by the this instance and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; + /// false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + DisposeInternal(disposing); + } + + #endregion Disposing + + #region Internal routines + + #region Reading + + /// + /// Read an unsigned short in little endian byte order. + /// + /// Returns the value read. + /// + /// The stream ends prematurely + /// + private ushort ReadLEUshort() + { + int data1 = baseStream_.ReadByte(); + + if (data1 < 0) + { + throw new EndOfStreamException("End of stream"); + } + + int data2 = baseStream_.ReadByte(); + + if (data2 < 0) + { + throw new EndOfStreamException("End of stream"); + } + + return unchecked((ushort)((ushort)data1 | (ushort)(data2 << 8))); + } + + /// + /// Read a uint in little endian byte order. + /// + /// Returns the value read. + /// + /// An i/o error occurs. + /// + /// + /// The file ends prematurely + /// + private uint ReadLEUint() + { + return (uint)(ReadLEUshort() | (ReadLEUshort() << 16)); + } + + private ulong ReadLEUlong() + { + return ReadLEUint() | ((ulong)ReadLEUint() << 32); + } + + #endregion Reading + + // NOTE this returns the offset of the first byte after the signature. + private long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData) + => ZipFormat.LocateBlockWithSignature(baseStream_, signature, endLocation, minimumBlockSize, maximumVariableData); + + /// + /// + /// + /// + public long GetCentralDirOffset() + { + // Search for the End Of Central Directory. When a zip comment is + // present the directory will start earlier + // + // The search is limited to 64K which is the maximum size of a trailing comment field to aid speed. + // This should be compatible with both SFX and ZIP files but has only been tested for Zip files + // If a SFX file has the Zip data attached as a resource and there are other resources occurring later then + // this could be invalid. + // Could also speed this up by reading memory in larger blocks. + + if (baseStream_.CanSeek == false) + { + throw new ZipException("ZipFile stream must be seekable"); + } + + long locatedEndOfCentralDir = LocateBlockWithSignature(ZipConstants.EndOfCentralDirectorySignature, + baseStream_.Length, ZipConstants.EndOfCentralRecordBaseSize, 0xffff); + + if (locatedEndOfCentralDir < 0) + { + throw new ZipException("Cannot find central directory"); + } + + // Read end of central directory record + ushort thisDiskNumber = ReadLEUshort(); + ushort startCentralDirDisk = ReadLEUshort(); + ulong entriesForThisDisk = ReadLEUshort(); + ulong entriesForWholeCentralDir = ReadLEUshort(); + ulong centralDirSize = ReadLEUint(); + long offsetOfCentralDir = ReadLEUint(); + uint commentSize = ReadLEUshort(); + + if (commentSize > 0) + { + byte[] comment = new byte[commentSize]; + + StreamUtils.ReadFully(baseStream_, comment); + comment_ = _stringCodec.ZipArchiveCommentEncoding.GetString(comment); + } + else + { + comment_ = string.Empty; + } + + bool isZip64 = false; + bool requireZip64 = false; + + // Check if zip64 header information is required. + if ((thisDiskNumber == 0xffff) || + (startCentralDirDisk == 0xffff) || + (entriesForThisDisk == 0xffff) || + (entriesForWholeCentralDir == 0xffff) || + (centralDirSize == 0xffffffff) || + (offsetOfCentralDir == 0xffffffff)) + { + requireZip64 = true; + } + + // #357 - always check for the existance of the Zip64 central directory. + // #403 - Take account of the fixed size of the locator when searching. + // Subtract from locatedEndOfCentralDir so that the endLocation is the location of EndOfCentralDirectorySignature, + // rather than the data following the signature. + long locatedZip64EndOfCentralDirLocator = LocateBlockWithSignature( + ZipConstants.Zip64CentralDirLocatorSignature, + locatedEndOfCentralDir - 4, + ZipConstants.Zip64EndOfCentralDirectoryLocatorSize, + 0); + + if (locatedZip64EndOfCentralDirLocator < 0) + { + if (requireZip64) + { + // This is only an error in cases where the Zip64 directory is required. + throw new ZipException("Cannot find Zip64 locator"); + } + } + else + { + isZip64 = true; + + // 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 + ReadLEUint(); // startDisk64 is not currently used + ulong offset64 = ReadLEUlong(); + uint totalDisks = ReadLEUint(); + + baseStream_.Position = (long)offset64; + long sig64 = ReadLEUint(); + + if (sig64 != ZipConstants.Zip64CentralFileHeaderSignature) + { + throw new ZipException(string.Format("Invalid Zip64 Central directory signature at {0:X}", offset64)); + } + + // NOTE: Record size = SizeOfFixedFields + SizeOfVariableData - 12. + ulong recordSize = ReadLEUlong(); + int versionMadeBy = ReadLEUshort(); + int versionToExtract = ReadLEUshort(); + uint thisDisk = ReadLEUint(); + uint centralDirDisk = ReadLEUint(); + entriesForThisDisk = ReadLEUlong(); + entriesForWholeCentralDir = ReadLEUlong(); + centralDirSize = ReadLEUlong(); + offsetOfCentralDir = (long)ReadLEUlong(); + + // NOTE: zip64 extensible data sector (variable size) is ignored. + } + + if (!isZip64 && (offsetOfCentralDir < locatedEndOfCentralDir - (4 + (long)centralDirSize))) + { + offsetOfFirstEntry = locatedEndOfCentralDir - (4 + (long)centralDirSize + offsetOfCentralDir); + if (offsetOfFirstEntry <= 0) + { + throw new ZipException("Invalid embedded zip archive"); + } + } + + return offsetOfFirstEntry + offsetOfCentralDir; + } + + /// + /// Search for and read the central directory of a zip file filling the entries array. + /// + /// + /// An i/o error occurs. + /// + /// + /// The central directory is malformed or cannot be found + /// + private void ReadEntries() + { + // Search for the End Of Central Directory. When a zip comment is + // present the directory will start earlier + // + // The search is limited to 64K which is the maximum size of a trailing comment field to aid speed. + // This should be compatible with both SFX and ZIP files but has only been tested for Zip files + // If a SFX file has the Zip data attached as a resource and there are other resources occurring later then + // this could be invalid. + // Could also speed this up by reading memory in larger blocks. + + if (baseStream_.CanSeek == false) + { + throw new ZipException("ZipFile stream must be seekable"); + } + + long locatedEndOfCentralDir = LocateBlockWithSignature(ZipConstants.EndOfCentralDirectorySignature, + baseStream_.Length, ZipConstants.EndOfCentralRecordBaseSize, 0xffff); + + if (locatedEndOfCentralDir < 0) + { + throw new ZipException("Cannot find central directory"); + } + + // Read end of central directory record + ushort thisDiskNumber = ReadLEUshort(); + ushort startCentralDirDisk = ReadLEUshort(); + ulong entriesForThisDisk = ReadLEUshort(); + ulong entriesForWholeCentralDir = ReadLEUshort(); + ulong centralDirSize = ReadLEUint(); + long offsetOfCentralDir = ReadLEUint(); + uint commentSize = ReadLEUshort(); + + if (commentSize > 0) + { + byte[] comment = new byte[commentSize]; + + StreamUtils.ReadFully(baseStream_, comment); + comment_ = _stringCodec.ZipArchiveCommentEncoding.GetString(comment); + } + else + { + comment_ = string.Empty; + } + + bool isZip64 = false; + bool requireZip64 = false; + + // Check if zip64 header information is required. + if ((thisDiskNumber == 0xffff) || + (startCentralDirDisk == 0xffff) || + (entriesForThisDisk == 0xffff) || + (entriesForWholeCentralDir == 0xffff) || + (centralDirSize == 0xffffffff) || + (offsetOfCentralDir == 0xffffffff)) + { + requireZip64 = true; + } + + // #357 - always check for the existance of the Zip64 central directory. + // #403 - Take account of the fixed size of the locator when searching. + // Subtract from locatedEndOfCentralDir so that the endLocation is the location of EndOfCentralDirectorySignature, + // rather than the data following the signature. + long locatedZip64EndOfCentralDirLocator = LocateBlockWithSignature( + ZipConstants.Zip64CentralDirLocatorSignature, + locatedEndOfCentralDir - 4, + ZipConstants.Zip64EndOfCentralDirectoryLocatorSize, + 0); + + if (locatedZip64EndOfCentralDirLocator < 0) + { + if (requireZip64) + { + // This is only an error in cases where the Zip64 directory is required. + throw new ZipException("Cannot find Zip64 locator"); + } + } + else + { + isZip64 = true; + + // 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 + ReadLEUint(); // startDisk64 is not currently used + ulong offset64 = ReadLEUlong(); + uint totalDisks = ReadLEUint(); + + baseStream_.Position = (long)offset64; + long sig64 = ReadLEUint(); + + if (sig64 != ZipConstants.Zip64CentralFileHeaderSignature) + { + throw new ZipException(string.Format("Invalid Zip64 Central directory signature at {0:X}", offset64)); + } + + // NOTE: Record size = SizeOfFixedFields + SizeOfVariableData - 12. + ulong recordSize = ReadLEUlong(); + int versionMadeBy = ReadLEUshort(); + int versionToExtract = ReadLEUshort(); + uint thisDisk = ReadLEUint(); + uint centralDirDisk = ReadLEUint(); + entriesForThisDisk = ReadLEUlong(); + entriesForWholeCentralDir = ReadLEUlong(); + centralDirSize = ReadLEUlong(); + offsetOfCentralDir = (long)ReadLEUlong(); + + // NOTE: zip64 extensible data sector (variable size) is ignored. + } + + entries_ = new ZipEntry[entriesForThisDisk]; + + // SFX/embedded support, find the offset of the first entry vis the start of the stream + // This applies to Zip files that are appended to the end of an SFX stub. + // Or are appended as a resource to an executable. + // Zip files created by some archivers have the offsets altered to reflect the true offsets + // and so dont require any adjustment here... + // TODO: Difficulty with Zip64 and SFX offset handling needs resolution - maths? + if (!isZip64 && (offsetOfCentralDir < locatedEndOfCentralDir - (4 + (long)centralDirSize))) + { + offsetOfFirstEntry = locatedEndOfCentralDir - (4 + (long)centralDirSize + offsetOfCentralDir); + if (offsetOfFirstEntry <= 0) + { + throw new ZipException("Invalid embedded zip archive"); + } + } + + baseStream_.Seek(offsetOfFirstEntry + offsetOfCentralDir, SeekOrigin.Begin); + + for (ulong i = 0; i < entriesForThisDisk; i++) + { + if (ReadLEUint() != ZipConstants.CentralHeaderSignature) + { + throw new ZipException("Wrong Central Directory signature"); + } + + int versionMadeBy = ReadLEUshort(); + int versionToExtract = ReadLEUshort(); + int bitFlags = ReadLEUshort(); + int method = ReadLEUshort(); + uint dostime = ReadLEUint(); + uint crc = ReadLEUint(); + var csize = (long)ReadLEUint(); + var size = (long)ReadLEUint(); + int nameLen = ReadLEUshort(); + int extraLen = ReadLEUshort(); + int commentLen = ReadLEUshort(); + + int diskStartNo = ReadLEUshort(); // Not currently used + int internalAttributes = ReadLEUshort(); // Not currently used + + uint externalAttributes = ReadLEUint(); + long offset = ReadLEUint(); + + byte[] buffer = new byte[Math.Max(nameLen, commentLen)]; + var entryEncoding = _stringCodec.ZipInputEncoding(bitFlags); + + StreamUtils.ReadFully(baseStream_, buffer, 0, nameLen); + string name = entryEncoding.GetString(buffer, 0, nameLen); + var unicode = entryEncoding.IsZipUnicode(); + + var entry = new ZipEntry(name, versionToExtract, versionMadeBy, (CompressionMethod)method, unicode) + { + Crc = crc & 0xffffffffL, + Size = size & 0xffffffffL, + CompressedSize = csize & 0xffffffffL, + Flags = bitFlags, + DosTime = dostime, + ZipFileIndex = (long)i, + Offset = offset, + ExternalFileAttributes = (int)externalAttributes + }; + + if ((bitFlags & 8) == 0) + { + entry.CryptoCheckValue = (byte)(crc >> 24); + } + else + { + entry.CryptoCheckValue = (byte)((dostime >> 8) & 0xff); + } + + if (extraLen > 0) + { + byte[] extra = new byte[extraLen]; + StreamUtils.ReadFully(baseStream_, extra); + entry.ExtraData = extra; + } + + entry.ProcessExtraData(false); + + if (commentLen > 0) + { + StreamUtils.ReadFully(baseStream_, buffer, 0, commentLen); + entry.Comment = entryEncoding.GetString(buffer, 0, commentLen); + } + + entries_[i] = entry; + } + } + + /// + /// Locate the data for a given entry. + /// + /// + /// The start offset of the data. + /// + /// + /// The stream ends prematurely + /// + /// + /// The local header signature is invalid, the entry and central header file name lengths are different + /// or the local and entry compression methods dont match + /// + private long LocateEntry(ZipEntry entry) + { + return TestLocalHeader(entry, HeaderTest.Extract); + } + + private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry) + { + CryptoStream result = null; + + if (entry.CompressionMethodForHeader == CompressionMethod.WinZipAES) + { + if (entry.Version >= ZipConstants.VERSION_AES) + { + // Issue #471 - accept an empty string as a password, but reject null. + OnKeysRequired(entry.Name); + if (rawPassword_ == null) + { + throw new ZipException("No password available for AES encrypted stream"); + } + int saltLen = entry.AESSaltLen; + byte[] saltBytes = new byte[saltLen]; + int saltIn = StreamUtils.ReadRequestedBytes(baseStream, saltBytes, 0, saltLen); + if (saltIn != saltLen) + throw new ZipException("AES Salt expected " + saltLen + " got " + saltIn); + // + byte[] pwdVerifyRead = new byte[2]; + StreamUtils.ReadFully(baseStream, pwdVerifyRead); + int blockSize = entry.AESKeySize / 8; // bits to bytes + + var decryptor = new ZipAESTransform(rawPassword_, saltBytes, blockSize, false); + byte[] pwdVerifyCalc = decryptor.PwdVerifier; + if (pwdVerifyCalc[0] != pwdVerifyRead[0] || pwdVerifyCalc[1] != pwdVerifyRead[1]) + throw new ZipException("Invalid password for AES"); + result = new ZipAESStream(baseStream, decryptor, CryptoStreamMode.Read); + } + else + { + throw new ZipException("Decryption method not supported"); + } + } + else + { + if ((entry.Version < ZipConstants.VersionStrongEncryption) + || (entry.Flags & (int)GeneralBitFlags.StrongEncryption) == 0) + { + var classicManaged = new PkzipClassicManaged(); + + OnKeysRequired(entry.Name); + if (HaveKeys == false) + { + throw new ZipException("No password available for encrypted stream"); + } + + result = new CryptoStream(baseStream, classicManaged.CreateDecryptor(key, null), CryptoStreamMode.Read); + CheckClassicPassword(result, entry); + } + else + { + // We don't support PKWare strong encryption + throw new ZipException("Decryption method not supported"); + } + } + + return result; + } + + private Stream CreateAndInitEncryptionStream(Stream baseStream, ZipEntry entry) + { + CryptoStream result = null; + if ((entry.Version < ZipConstants.VersionStrongEncryption) + || (entry.Flags & (int)GeneralBitFlags.StrongEncryption) == 0) + { + var classicManaged = new PkzipClassicManaged(); + + OnKeysRequired(entry.Name); + if (HaveKeys == false) + { + throw new ZipException("No password available for encrypted stream"); + } + + // Closing a CryptoStream will close the base stream as well so wrap it in an UncompressedStream + // which doesnt do this. + result = new CryptoStream(new UncompressedStream(baseStream), + classicManaged.CreateEncryptor(key, null), CryptoStreamMode.Write); + + if ((entry.Crc < 0) || (entry.Flags & 8) != 0) + { + WriteEncryptionHeader(result, entry.DosTime << 16); + } + else + { + WriteEncryptionHeader(result, entry.Crc); + } + } + return result; + } + + private static void CheckClassicPassword(CryptoStream classicCryptoStream, ZipEntry entry) + { + byte[] cryptbuffer = new byte[ZipConstants.CryptoHeaderSize]; + StreamUtils.ReadFully(classicCryptoStream, cryptbuffer); + if (cryptbuffer[ZipConstants.CryptoHeaderSize - 1] != entry.CryptoCheckValue) + { + throw new ZipException("Invalid password"); + } + } + + private static void WriteEncryptionHeader(Stream stream, long crcValue) + { + byte[] cryptBuffer = new byte[ZipConstants.CryptoHeaderSize]; + using (var rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(cryptBuffer); + } + cryptBuffer[11] = (byte)(crcValue >> 24); + stream.Write(cryptBuffer, 0, cryptBuffer.Length); + } + + #endregion Internal routines + + #region Instance Fields + + private bool isDisposed_; + private string name_; + private string comment_ = string.Empty; + private string rawPassword_; + private Stream baseStream_; + private bool isStreamOwner; + private long offsetOfFirstEntry; + private ZipEntry[] entries_; + private byte[] key; + private bool isNewArchive_; + private StringCodec _stringCodec = ZipStrings.GetStringCodec(); + + // Default is dynamic which is not backwards compatible and can cause problems + // with XP's built in compression which cant read Zip64 archives. + // However it does avoid the situation were a large file is added and cannot be completed correctly. + // Hint: Set always ZipEntry size before they are added to an archive and this setting isnt needed. + private UseZip64 useZip64_ = UseZip64.Dynamic; + + #region Zip Update Instance Fields + + private List updates_; + private long updateCount_; // Count is managed manually as updates_ can contain nulls! + private Dictionary updateIndex_; + private IArchiveStorage archiveStorage_; + private IDynamicDataSource updateDataSource_; + private bool contentsEdited_; + private int bufferSize_ = DefaultBufferSize; + private byte[] copyBuffer_; + private ZipString newComment_; + private bool commentEdited_; + private IEntryFactory updateEntryFactory_ = new ZipEntryFactory(); + + #endregion Zip Update Instance Fields + + #endregion Instance Fields + + #region Support Classes + + /// + /// Represents a string from a which is stored as an array of bytes. + /// + private class ZipString + { + #region Constructors + + /// + /// Initialise a with a string. + /// + /// The textual string form. + /// + public ZipString(string comment, Encoding encoding) + { + comment_ = comment; + isSourceString_ = true; + _encoding = encoding; + } + + /// + /// Initialise a using a string in its binary 'raw' form. + /// + /// + /// + public ZipString(byte[] rawString, Encoding encoding) + { + rawComment_ = rawString; + _encoding = encoding; + } + + #endregion Constructors + + /// + /// Get a value indicating the original source of data for this instance. + /// True if the source was a string; false if the source was binary data. + /// + public bool IsSourceString => isSourceString_; + + /// + /// Get the length of the comment when represented as raw bytes. + /// + public int RawLength + { + get + { + MakeBytesAvailable(); + return rawComment_.Length; + } + } + + /// + /// Get the comment in its 'raw' form as plain bytes. + /// + public byte[] RawComment + { + get + { + MakeBytesAvailable(); + return (byte[])rawComment_.Clone(); + } + } + + /// + /// Reset the comment to its initial state. + /// + public void Reset() + { + if (isSourceString_) + { + rawComment_ = null; + } + else + { + comment_ = null; + } + } + + private void MakeTextAvailable() + { + if (comment_ == null) + { + comment_ = _encoding.GetString(rawComment_); + } + } + + private void MakeBytesAvailable() + { + if (rawComment_ == null) + { + rawComment_ = _encoding.GetBytes(comment_); + } + } + + /// + /// Implicit conversion of comment to a string. + /// + /// The to convert to a string. + /// The textual equivalent for the input value. + public static implicit operator string(ZipString zipString) + { + zipString.MakeTextAvailable(); + return zipString.comment_; + } + + #region Instance Fields + + private string comment_; + private byte[] rawComment_; + private readonly bool isSourceString_; + private readonly Encoding _encoding; + + #endregion Instance Fields + } + + /// + /// An enumerator for Zip entries + /// + private class ZipEntryEnumerator : IEnumerator + { + #region Constructors + + public ZipEntryEnumerator(ZipEntry[] entries) + { + array = entries; + } + + #endregion Constructors + + #region IEnumerator Members + + public object Current + { + get + { + return array[index]; + } + } + + public void Reset() + { + index = -1; + } + + public bool MoveNext() + { + return (++index < array.Length); + } + + #endregion IEnumerator Members + + #region Instance Fields + + private ZipEntry[] array; + private int index = -1; + + #endregion Instance Fields + } + + /// + /// An is a stream that you can write uncompressed data + /// to and flush, but cannot read, seek or do anything else to. + /// + private class UncompressedStream : Stream + { + #region Constructors + + public UncompressedStream(Stream baseStream) + { + baseStream_ = baseStream; + } + + #endregion Constructors + + /// + /// Gets a value indicating whether the current stream supports reading. + /// + public override bool CanRead + { + get + { + return false; + } + } + + /// + /// Write any buffered data to underlying storage. + /// + public override void Flush() + { + baseStream_.Flush(); + } + + /// + /// Gets a value indicating whether the current stream supports writing. + /// + public override bool CanWrite + { + get + { + return baseStream_.CanWrite; + } + } + + /// + /// Gets a value indicating whether the current stream supports seeking. + /// + public override bool CanSeek + { + get + { + return false; + } + } + + /// + /// Get the length in bytes of the stream. + /// + public override long Length + { + get + { + return 0; + } + } + + /// + /// Gets or sets the position within the current stream. + /// + public override long Position + { + get + { + return baseStream_.Position; + } + set + { + throw new NotImplementedException(); + } + } + + /// + /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. + /// + /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. + /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. + /// The maximum number of bytes to be read from the current stream. + /// + /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. + /// + /// The sum of offset and count is larger than the buffer length. + /// Methods were called after the stream was closed. + /// The stream does not support reading. + /// buffer is null. + /// An I/O error occurs. + /// offset or count is negative. + public override int Read(byte[] buffer, int offset, int count) + { + return 0; + } + + /// + /// Sets the position within the current stream. + /// + /// A byte offset relative to the origin parameter. + /// A value of type indicating the reference point used to obtain the new position. + /// + /// The new position within the current stream. + /// + /// An I/O error occurs. + /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. + /// Methods were called after the stream was closed. + public override long Seek(long offset, SeekOrigin origin) + { + return 0; + } + + /// + /// Sets the length of the current stream. + /// + /// The desired length of the current stream in bytes. + /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. + /// An I/O error occurs. + /// Methods were called after the stream was closed. + public override void SetLength(long value) + { + } + + /// + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// An array of bytes. This method copies count bytes from buffer to the current stream. + /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. + /// The number of bytes to be written to the current stream. + /// An I/O error occurs. + /// The stream does not support writing. + /// Methods were called after the stream was closed. + /// buffer is null. + /// The sum of offset and count is greater than the buffer length. + /// offset or count is negative. + public override void Write(byte[] buffer, int offset, int count) + { + baseStream_.Write(buffer, offset, count); + } + + private readonly + + #region Instance Fields + + Stream baseStream_; + + #endregion Instance Fields + } + + /// + /// A is an + /// whose data is only a part or subsection of a file. + /// + private class PartialInputStream : Stream + { + #region Constructors + + /// + /// Initialise a new instance of the class. + /// + /// The containing the underlying stream to use for IO. + /// The start of the partial data. + /// The length of the partial data. + public PartialInputStream(ZipFile zipFile, long start, long length) + { + start_ = start; + length_ = length; + + // Although this is the only time the zipfile is used + // keeping a reference here prevents premature closure of + // this zip file and thus the baseStream_. + + // Code like this will cause apparently random failures depending + // on the size of the files and when garbage is collected. + // + // ZipFile z = new ZipFile (stream); + // Stream reader = z.GetInputStream(0); + // uses reader here.... + zipFile_ = zipFile; + baseStream_ = zipFile_.baseStream_; + readPos_ = start; + end_ = start + length; + } + + #endregion Constructors + + /// + /// Read a byte from this stream. + /// + /// Returns the byte read or -1 on end of stream. + public override int ReadByte() + { + if (readPos_ >= end_) + { + // -1 is the correct value at end of stream. + return -1; + } + + lock (baseStream_) + { + baseStream_.Seek(readPos_++, SeekOrigin.Begin); + return baseStream_.ReadByte(); + } + } + + /// + /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. + /// + /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. + /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. + /// The maximum number of bytes to be read from the current stream. + /// + /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. + /// + /// The sum of offset and count is larger than the buffer length. + /// Methods were called after the stream was closed. + /// The stream does not support reading. + /// buffer is null. + /// An I/O error occurs. + /// offset or count is negative. + public override int Read(byte[] buffer, int offset, int count) + { + lock (baseStream_) + { + if (count > end_ - readPos_) + { + count = (int)(end_ - readPos_); + if (count == 0) + { + return 0; + } + } + // Protect against Stream implementations that throw away their buffer on every Seek + // (for example, Mono FileStream) + if (baseStream_.Position != readPos_) + { + baseStream_.Seek(readPos_, SeekOrigin.Begin); + } + int readCount = baseStream_.Read(buffer, offset, count); + if (readCount > 0) + { + readPos_ += readCount; + } + return readCount; + } + } + + /// + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// An array of bytes. This method copies count bytes from buffer to the current stream. + /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. + /// The number of bytes to be written to the current stream. + /// An I/O error occurs. + /// The stream does not support writing. + /// Methods were called after the stream was closed. + /// buffer is null. + /// The sum of offset and count is greater than the buffer length. + /// offset or count is negative. + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class, sets the length of the current stream. + /// + /// The desired length of the current stream in bytes. + /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. + /// An I/O error occurs. + /// Methods were called after the stream was closed. + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// + /// When overridden in a derived class, sets the position within the current stream. + /// + /// A byte offset relative to the origin parameter. + /// A value of type indicating the reference point used to obtain the new position. + /// + /// The new position within the current stream. + /// + /// An I/O error occurs. + /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. + /// Methods were called after the stream was closed. + public override long Seek(long offset, SeekOrigin origin) + { + long newPos = readPos_; + + switch (origin) + { + case SeekOrigin.Begin: + newPos = start_ + offset; + break; + + case SeekOrigin.Current: + newPos = readPos_ + offset; + break; + + case SeekOrigin.End: + newPos = end_ + offset; + break; + } + + if (newPos < start_) + { + throw new ArgumentException("Negative position is invalid"); + } + + if (newPos > end_) + { + throw new IOException("Cannot seek past end"); + } + readPos_ = newPos; + return readPos_; + } + + /// + /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. + /// + /// An I/O error occurs. + public override void Flush() + { + // Nothing to do. + } + + /// + /// Gets or sets the position within the current stream. + /// + /// + /// The current position within the stream. + /// An I/O error occurs. + /// The stream does not support seeking. + /// Methods were called after the stream was closed. + public override long Position + { + get { return readPos_ - start_; } + set + { + long newPos = start_ + value; + + if (newPos < start_) + { + throw new ArgumentException("Negative position is invalid"); + } + + if (newPos > end_) + { + throw new InvalidOperationException("Cannot seek past end"); + } + readPos_ = newPos; + } + } + + /// + /// Gets the length in bytes of the stream. + /// + /// + /// A long value representing the length of the stream in bytes. + /// A class derived from Stream does not support seeking. + /// Methods were called after the stream was closed. + public override long Length + { + get { return length_; } + } + + /// + /// Gets a value indicating whether the current stream supports writing. + /// + /// false + /// true if the stream supports writing; otherwise, false. + public override bool CanWrite + { + get { return false; } + } + + /// + /// Gets a value indicating whether the current stream supports seeking. + /// + /// true + /// true if the stream supports seeking; otherwise, false. + public override bool CanSeek + { + get { return true; } + } + + /// + /// Gets a value indicating whether the current stream supports reading. + /// + /// true. + /// true if the stream supports reading; otherwise, false. + public override bool CanRead + { + get { return true; } + } + + /// + /// Gets a value that determines whether the current stream can time out. + /// + /// + /// A value that determines whether the current stream can time out. + public override bool CanTimeout + { + get { return baseStream_.CanTimeout; } + } + + #region Instance Fields + + private ZipFile zipFile_; + private Stream baseStream_; + private readonly long start_; + private readonly long length_; + private long readPos_; + private readonly long end_; + + #endregion Instance Fields + } + + #endregion Support Classes + } + + #endregion ZipFile Class + + #region DataSources + + /// + /// Provides a static way to obtain a source of data for an entry. + /// + public interface IStaticDataSource + { + /// + /// Get a source of data by creating a new stream. + /// + /// Returns a to use for compression input. + /// Ideally a new stream is created and opened to achieve this, to avoid locking problems. + Stream GetSource(); + } + + /// + /// Represents a source of data that can dynamically provide + /// multiple data sources based on the parameters passed. + /// + public interface IDynamicDataSource + { + /// + /// Get a data source. + /// + /// The to get a source for. + /// The name for data if known. + /// Returns a to use for compression input. + /// Ideally a new stream is created and opened to achieve this, to avoid locking problems. + Stream GetSource(ZipEntry entry, string name); + } + + /// + /// Default implementation of a for use with files stored on disk. + /// + public class StaticDiskDataSource : IStaticDataSource + { + /// + /// Initialise a new instance of + /// + /// The name of the file to obtain data from. + public StaticDiskDataSource(string fileName) + { + fileName_ = fileName; + } + + #region IDataSource Members + + /// + /// Get a providing data. + /// + /// Returns a providing data. + public Stream GetSource() + { + return File.Open(fileName_, FileMode.Open, FileAccess.Read, FileShare.Read); + } + + private readonly + + #endregion IDataSource Members + + #region Instance Fields + + string fileName_; + + #endregion Instance Fields + } + + /// + /// Default implementation of for files stored on disk. + /// + public class DynamicDiskDataSource : IDynamicDataSource + { + #region IDataSource Members + + /// + /// Get a providing data for an entry. + /// + /// The entry to provide data for. + /// The file name for data if known. + /// Returns a stream providing data; or null if not available + public Stream GetSource(ZipEntry entry, string name) + { + Stream result = null; + + if (name != null) + { + result = File.Open(name, FileMode.Open, FileAccess.Read, FileShare.Read); + } + + return result; + } + + #endregion IDataSource Members + } + + #endregion DataSources + + #region Archive Storage + + /// + /// Defines facilities for data storage when updating Zip Archives. + /// + public interface IArchiveStorage + { + /// + /// Get the to apply during updates. + /// + FileUpdateMode UpdateMode { get; } + + /// + /// Get an empty that can be used for temporary output. + /// + /// Returns a temporary output + /// + Stream GetTemporaryOutput(); + + /// + /// Convert a temporary output stream to a final stream. + /// + /// The resulting final + /// + Stream ConvertTemporaryToFinal(); + + /// + /// Make a temporary copy of the original stream. + /// + /// The to copy. + /// Returns a temporary output that is a copy of the input. + Stream MakeTemporaryCopy(Stream stream); + + /// + /// Return a stream suitable for performing direct updates on the original source. + /// + /// The current stream. + /// Returns a stream suitable for direct updating. + /// This may be the current stream passed. + Stream OpenForDirectUpdate(Stream stream); + + /// + /// Dispose of this instance. + /// + void Dispose(); + } + + /// + /// An abstract suitable for extension by inheritance. + /// + abstract public class BaseArchiveStorage : IArchiveStorage + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The update mode. + protected BaseArchiveStorage(FileUpdateMode updateMode) + { + updateMode_ = updateMode; + } + + #endregion Constructors + + #region IArchiveStorage Members + + /// + /// Gets a temporary output + /// + /// Returns the temporary output stream. + /// + public abstract Stream GetTemporaryOutput(); + + /// + /// Converts the temporary to its final form. + /// + /// Returns a that can be used to read + /// the final storage for the archive. + /// + public abstract Stream ConvertTemporaryToFinal(); + + /// + /// Make a temporary copy of a . + /// + /// The to make a copy of. + /// Returns a temporary output that is a copy of the input. + public abstract Stream MakeTemporaryCopy(Stream stream); + + /// + /// Return a stream suitable for performing direct updates on the original source. + /// + /// The to open for direct update. + /// Returns a stream suitable for direct updating. + public abstract Stream OpenForDirectUpdate(Stream stream); + + /// + /// Disposes this instance. + /// + public abstract void Dispose(); + + /// + /// Gets the update mode applicable. + /// + /// The update mode. + public FileUpdateMode UpdateMode + { + get + { + return updateMode_; + } + } + + #endregion IArchiveStorage Members + + #region Instance Fields + + private readonly FileUpdateMode updateMode_; + + #endregion Instance Fields + } + + /// + /// An implementation suitable for hard disks. + /// + public class DiskArchiveStorage : BaseArchiveStorage + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The file. + /// The update mode. + public DiskArchiveStorage(ZipFile file, FileUpdateMode updateMode) + : base(updateMode) + { + if (file.Name == null) + { + throw new ZipException("Cant handle non file archives"); + } + + fileName_ = file.Name; + } + + /// + /// Initializes a new instance of the class. + /// + /// The file. + public DiskArchiveStorage(ZipFile file) + : this(file, FileUpdateMode.Safe) + { + } + + #endregion Constructors + + #region IArchiveStorage Members + + /// + /// Gets a temporary output for performing updates on. + /// + /// Returns the temporary output stream. + public override Stream GetTemporaryOutput() + { + temporaryName_ = PathUtils.GetTempFileName(temporaryName_); + temporaryStream_ = File.Open(temporaryName_, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); + + return temporaryStream_; + } + + /// + /// Converts a temporary to its final form. + /// + /// Returns a that can be used to read + /// the final storage for the archive. + public override Stream ConvertTemporaryToFinal() + { + if (temporaryStream_ == null) + { + throw new ZipException("No temporary stream has been created"); + } + + Stream result = null; + + string moveTempName = PathUtils.GetTempFileName(fileName_); + bool newFileCreated = false; + + try + { + temporaryStream_.Dispose(); + File.Move(fileName_, moveTempName); + File.Move(temporaryName_, fileName_); + newFileCreated = true; + File.Delete(moveTempName); + + result = File.Open(fileName_, FileMode.Open, FileAccess.Read, FileShare.Read); + } + catch (Exception) + { + result = null; + + // Try to roll back changes... + if (!newFileCreated) + { + File.Move(moveTempName, fileName_); + File.Delete(temporaryName_); + } + + throw; + } + + return result; + } + + /// + /// Make a temporary copy of a stream. + /// + /// The to copy. + /// Returns a temporary output that is a copy of the input. + public override Stream MakeTemporaryCopy(Stream stream) + { + stream.Dispose(); + + temporaryName_ = PathUtils.GetTempFileName(fileName_); + File.Copy(fileName_, temporaryName_, true); + + temporaryStream_ = new FileStream(temporaryName_, + FileMode.Open, + FileAccess.ReadWrite); + return temporaryStream_; + } + + /// + /// Return a stream suitable for performing direct updates on the original source. + /// + /// The current stream. + /// Returns a stream suitable for direct updating. + /// If the is not null this is used as is. + public override Stream OpenForDirectUpdate(Stream stream) + { + Stream result; + if ((stream == null) || !stream.CanWrite) + { + if (stream != null) + { + stream.Dispose(); + } + + result = new FileStream(fileName_, + FileMode.Open, + FileAccess.ReadWrite); + } + else + { + result = stream; + } + + return result; + } + + /// + /// Disposes this instance. + /// + public override void Dispose() + { + if (temporaryStream_ != null) + { + temporaryStream_.Dispose(); + } + } + + #endregion IArchiveStorage Members + + #region Instance Fields + + private Stream temporaryStream_; + private readonly string fileName_; + private string temporaryName_; + + #endregion Instance Fields + } + + /// + /// An implementation suitable for in memory streams. + /// + public class MemoryArchiveStorage : BaseArchiveStorage + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public MemoryArchiveStorage() + : base(FileUpdateMode.Direct) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The to use + /// This constructor is for testing as memory streams dont really require safe mode. + public MemoryArchiveStorage(FileUpdateMode updateMode) + : base(updateMode) + { + } + + #endregion Constructors + + #region Properties + + /// + /// Get the stream returned by if this was in fact called. + /// + public MemoryStream FinalStream + { + get { return finalStream_; } + } + + #endregion Properties + + #region IArchiveStorage Members + + /// + /// Gets the temporary output + /// + /// Returns the temporary output stream. + public override Stream GetTemporaryOutput() + { + temporaryStream_ = new MemoryStream(); + return temporaryStream_; + } + + /// + /// Converts the temporary to its final form. + /// + /// Returns a that can be used to read + /// the final storage for the archive. + public override Stream ConvertTemporaryToFinal() + { + if (temporaryStream_ == null) + { + throw new ZipException("No temporary stream has been created"); + } + + finalStream_ = new MemoryStream(temporaryStream_.ToArray()); + return finalStream_; + } + + /// + /// Make a temporary copy of the original stream. + /// + /// The to copy. + /// Returns a temporary output that is a copy of the input. + public override Stream MakeTemporaryCopy(Stream stream) + { + temporaryStream_ = new MemoryStream(); + stream.Position = 0; + StreamUtils.Copy(stream, temporaryStream_, new byte[4096]); + return temporaryStream_; + } + + /// + /// Return a stream suitable for performing direct updates on the original source. + /// + /// The original source stream + /// Returns a stream suitable for direct updating. + /// If the passed is not null this is used; + /// otherwise a new is returned. + public override Stream OpenForDirectUpdate(Stream stream) + { + Stream result; + if ((stream == null) || !stream.CanWrite) + { + result = new MemoryStream(); + + if (stream != null) + { + stream.Position = 0; + StreamUtils.Copy(stream, result, new byte[4096]); + + stream.Dispose(); + } + } + else + { + result = stream; + } + + return result; + } + + /// + /// Disposes this instance. + /// + public override void Dispose() + { + if (temporaryStream_ != null) + { + temporaryStream_.Dispose(); + } + } + + #endregion IArchiveStorage Members + + #region Instance Fields + + private MemoryStream temporaryStream_; + private MemoryStream finalStream_; + + #endregion Instance Fields + } + + #endregion Archive Storage +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs b/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs new file mode 100644 index 000000000000..a37ab3031438 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs @@ -0,0 +1,597 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using ICSharpCode.SharpZipLib.Core; + +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// Holds data pertinent to a data descriptor. + /// + public class DescriptorData + { + private long _crc; + + /// + /// Get /set the compressed size of data. + /// + public long CompressedSize { get; set; } + + /// + /// Get / set the uncompressed size of data + /// + public long Size { get; set; } + + /// + /// Get /set the crc value. + /// + public long Crc + { + get => _crc; + set => _crc = (value & 0xffffffff); + } + } + + internal struct EntryPatchData + { + public long SizePatchOffset { get; set; } + + public long CrcPatchOffset { get; set; } + } + + /// + /// This class assists with writing/reading from Zip files. + /// + internal static class ZipFormat + { + // Write the local file header + // TODO: ZipFormat.WriteLocalHeader is not yet used and needs checking for ZipFile and ZipOuptutStream usage + internal static int WriteLocalHeader(Stream stream, ZipEntry entry, out EntryPatchData patchData, + bool headerInfoAvailable, bool patchEntryHeader, long streamOffset, StringCodec stringCodec) + { + patchData = new EntryPatchData(); + + stream.WriteLEInt(ZipConstants.LocalHeaderSignature); + stream.WriteLEShort(entry.Version); + stream.WriteLEShort(entry.Flags); + stream.WriteLEShort((byte)entry.CompressionMethodForHeader); + stream.WriteLEInt((int)entry.DosTime); + + if (headerInfoAvailable) + { + stream.WriteLEInt((int)entry.Crc); + if (entry.LocalHeaderRequiresZip64) + { + stream.WriteLEInt(-1); + stream.WriteLEInt(-1); + } + else + { + stream.WriteLEInt((int)entry.CompressedSize + entry.EncryptionOverheadSize); + stream.WriteLEInt((int)entry.Size); + } + } + else + { + if (patchEntryHeader) + patchData.CrcPatchOffset = streamOffset + stream.Position; + + stream.WriteLEInt(0); // Crc + + if (patchEntryHeader) + patchData.SizePatchOffset = streamOffset + stream.Position; + + // For local header both sizes appear in Zip64 Extended Information + if (entry.LocalHeaderRequiresZip64 && patchEntryHeader) + { + stream.WriteLEInt(-1); + stream.WriteLEInt(-1); + } + else + { + stream.WriteLEInt(0); // Compressed size + stream.WriteLEInt(0); // Uncompressed size + } + } + + byte[] name = stringCodec.ZipOutputEncoding.GetBytes(entry.Name); + + if (name.Length > 0xFFFF) + { + throw new ZipException("Entry name too long."); + } + + var ed = new ZipExtraData(entry.ExtraData); + + if (entry.LocalHeaderRequiresZip64) + { + ed.StartNewEntry(); + if (headerInfoAvailable) + { + ed.AddLeLong(entry.Size); + ed.AddLeLong(entry.CompressedSize + entry.EncryptionOverheadSize); + } + else + { + ed.AddLeLong(-1); + ed.AddLeLong(-1); + } + ed.AddNewEntry(1); + + if (!ed.Find(1)) + { + throw new ZipException("Internal error cant find extra data"); + } + + patchData.SizePatchOffset = ed.CurrentReadIndex; + } + else + { + ed.Delete(1); + } + + if (entry.AESKeySize > 0) + { + AddExtraDataAES(entry, ed); + } + byte[] extra = ed.GetEntryData(); + + stream.WriteLEShort(name.Length); + stream.WriteLEShort(extra.Length); + + if (name.Length > 0) + { + stream.Write(name, 0, name.Length); + } + + if (entry.LocalHeaderRequiresZip64 && patchEntryHeader) + { + patchData.SizePatchOffset += streamOffset + stream.Position; + } + + if (extra.Length > 0) + { + stream.Write(extra, 0, extra.Length); + } + + return ZipConstants.LocalHeaderBaseSize + name.Length + extra.Length; + } + + /// + /// Locates a block with the desired . + /// + /// + /// The signature to find. + /// Location, marking the end of block. + /// Minimum size of the block. + /// The maximum variable data. + /// Returns the offset of the first byte after the signature; -1 if not found + internal static long LocateBlockWithSignature(Stream stream, int signature, long endLocation, int minimumBlockSize, int maximumVariableData) + { + long pos = endLocation - minimumBlockSize; + if (pos < 0) + { + return -1; + } + + long giveUpMarker = Math.Max(pos - maximumVariableData, 0); + + // TODO: This loop could be optimized for speed. + do + { + if (pos < giveUpMarker) + { + return -1; + } + stream.Seek(pos--, SeekOrigin.Begin); + } while (stream.ReadLEInt() != signature); + + return stream.Position; + } + + /// + public static async Task WriteZip64EndOfCentralDirectoryAsync(Stream stream, long noOfEntries, + long sizeEntries, long centralDirOffset, CancellationToken cancellationToken) + { + await stream.WriteProcToStreamAsync(s => WriteZip64EndOfCentralDirectory(s, noOfEntries, sizeEntries, centralDirOffset), cancellationToken); + } + + /// + /// Write Zip64 end of central directory records (File header and locator). + /// + /// + /// The number of entries in the central directory. + /// The size of entries in the central directory. + /// The offset of the central directory. + internal static void WriteZip64EndOfCentralDirectory(Stream stream, long noOfEntries, long sizeEntries, long centralDirOffset) + { + long centralSignatureOffset = centralDirOffset + sizeEntries; + stream.WriteLEInt(ZipConstants.Zip64CentralFileHeaderSignature); + stream.WriteLELong(44); // Size of this record (total size of remaining fields in header or full size - 12) + stream.WriteLEShort(ZipConstants.VersionMadeBy); // Version made by + stream.WriteLEShort(ZipConstants.VersionZip64); // Version to extract + stream.WriteLEInt(0); // Number of this disk + stream.WriteLEInt(0); // number of the disk with the start of the central directory + stream.WriteLELong(noOfEntries); // No of entries on this disk + stream.WriteLELong(noOfEntries); // Total No of entries in central directory + stream.WriteLELong(sizeEntries); // Size of the central directory + stream.WriteLELong(centralDirOffset); // offset of start of central directory + // zip64 extensible data sector not catered for here (variable size) + + // Write the Zip64 end of central directory locator + stream.WriteLEInt(ZipConstants.Zip64CentralDirLocatorSignature); + + // no of the disk with the start of the zip64 end of central directory + stream.WriteLEInt(0); + + // relative offset of the zip64 end of central directory record + stream.WriteLELong(centralSignatureOffset); + + // total number of disks + stream.WriteLEInt(1); + } + + /// + public static async Task WriteEndOfCentralDirectoryAsync(Stream stream, long noOfEntries, long sizeEntries, + long start, byte[] comment, CancellationToken cancellationToken) + => await stream.WriteProcToStreamAsync(s + => WriteEndOfCentralDirectory(s, noOfEntries, sizeEntries, start, comment), cancellationToken); + + /// + /// Write the required records to end the central directory. + /// + /// + /// The number of entries in the directory. + /// The size of the entries in the directory. + /// The start of the central directory. + /// The archive comment. (This can be null). + + internal static void WriteEndOfCentralDirectory(Stream stream, long noOfEntries, long sizeEntries, long start, byte[] comment) + { + if (noOfEntries >= 0xffff || + start >= 0xffffffff || + sizeEntries >= 0xffffffff) + { + WriteZip64EndOfCentralDirectory(stream, noOfEntries, sizeEntries, start); + } + + stream.WriteLEInt(ZipConstants.EndOfCentralDirectorySignature); + + // TODO: ZipFile Multi disk handling not done + stream.WriteLEShort(0); // number of this disk + stream.WriteLEShort(0); // no of disk with start of central dir + + // Number of entries + if (noOfEntries >= 0xffff) + { + stream.WriteLEUshort(0xffff); // Zip64 marker + stream.WriteLEUshort(0xffff); + } + else + { + stream.WriteLEShort((short)noOfEntries); // entries in central dir for this disk + stream.WriteLEShort((short)noOfEntries); // total entries in central directory + } + + // Size of the central directory + if (sizeEntries >= 0xffffffff) + { + stream.WriteLEUint(0xffffffff); // Zip64 marker + } + else + { + stream.WriteLEInt((int)sizeEntries); + } + + // offset of start of central directory + if (start >= 0xffffffff) + { + stream.WriteLEUint(0xffffffff); // Zip64 marker + } + else + { + stream.WriteLEInt((int)start); + } + + var commentLength = comment?.Length ?? 0; + + if (commentLength > 0xffff) + { + throw new ZipException($"Comment length ({commentLength}) is larger than 64K"); + } + + stream.WriteLEShort(commentLength); + + if (commentLength > 0) + { + stream.Write(comment, 0, commentLength); + } + } + + + + /// + /// Write a data descriptor. + /// + /// + /// The entry to write a descriptor for. + /// Returns the number of descriptor bytes written. + internal static int WriteDataDescriptor(Stream stream, ZipEntry entry) + { + if (entry == null) + { + throw new ArgumentNullException(nameof(entry)); + } + + int result = 0; + + // Add data descriptor if flagged as required + if ((entry.Flags & (int)GeneralBitFlags.Descriptor) != 0) + { + // The signature is not PKZIP originally but is now described as optional + // in the PKZIP Appnote documenting the format. + stream.WriteLEInt(ZipConstants.DataDescriptorSignature); + stream.WriteLEInt(unchecked((int)(entry.Crc))); + + result += 8; + + if (entry.LocalHeaderRequiresZip64) + { + stream.WriteLELong(entry.CompressedSize); + stream.WriteLELong(entry.Size); + result += 16; + } + else + { + stream.WriteLEInt((int)entry.CompressedSize); + stream.WriteLEInt((int)entry.Size); + result += 8; + } + } + + return result; + } + + /// + /// Read data descriptor at the end of compressed data. + /// + /// + /// if set to true [zip64]. + /// The data to fill in. + /// Returns the number of bytes read in the descriptor. + internal static void ReadDataDescriptor(Stream stream, bool zip64, DescriptorData data) + { + int intValue = stream.ReadLEInt(); + + // In theory this may not be a descriptor according to PKZIP appnote. + // In practice its always there. + if (intValue != ZipConstants.DataDescriptorSignature) + { + throw new ZipException("Data descriptor signature not found"); + } + + data.Crc = stream.ReadLEInt(); + + if (zip64) + { + data.CompressedSize = stream.ReadLELong(); + data.Size = stream.ReadLELong(); + } + else + { + data.CompressedSize = stream.ReadLEInt(); + data.Size = stream.ReadLEInt(); + } + } + + internal static int WriteEndEntry(Stream stream, ZipEntry entry, StringCodec stringCodec) + { + stream.WriteLEInt(ZipConstants.CentralHeaderSignature); + stream.WriteLEShort((entry.HostSystem << 8) | entry.VersionMadeBy); + stream.WriteLEShort(entry.Version); + stream.WriteLEShort(entry.Flags); + stream.WriteLEShort((short)entry.CompressionMethodForHeader); + stream.WriteLEInt((int)entry.DosTime); + stream.WriteLEInt((int)entry.Crc); + + if (entry.IsZip64Forced() || + (entry.CompressedSize >= uint.MaxValue)) + { + stream.WriteLEInt(-1); + } + else + { + stream.WriteLEInt((int)entry.CompressedSize); + } + + if (entry.IsZip64Forced() || + (entry.Size >= uint.MaxValue)) + { + stream.WriteLEInt(-1); + } + else + { + stream.WriteLEInt((int)entry.Size); + } + + byte[] name = stringCodec.ZipOutputEncoding.GetBytes(entry.Name); + + if (name.Length > 0xffff) + { + throw new ZipException("Name too long."); + } + + var ed = new ZipExtraData(entry.ExtraData); + + if (entry.CentralHeaderRequiresZip64) + { + ed.StartNewEntry(); + if (entry.IsZip64Forced() || + (entry.Size >= 0xffffffff)) + { + ed.AddLeLong(entry.Size); + } + + if (entry.IsZip64Forced() || + (entry.CompressedSize >= 0xffffffff)) + { + ed.AddLeLong(entry.CompressedSize); + } + + if (entry.Offset >= 0xffffffff) + { + ed.AddLeLong(entry.Offset); + } + + ed.AddNewEntry(1); + } + else + { + ed.Delete(1); + } + + if (entry.AESKeySize > 0) + { + AddExtraDataAES(entry, ed); + } + byte[] extra = ed.GetEntryData(); + + byte[] entryComment = !(entry.Comment is null) + ? stringCodec.ZipOutputEncoding.GetBytes(entry.Comment) + : Empty.Array(); + + if (entryComment.Length > 0xffff) + { + throw new ZipException("Comment too long."); + } + + stream.WriteLEShort(name.Length); + stream.WriteLEShort(extra.Length); + stream.WriteLEShort(entryComment.Length); + stream.WriteLEShort(0); // disk number + stream.WriteLEShort(0); // internal file attributes + // external file attributes + + if (entry.ExternalFileAttributes != -1) + { + stream.WriteLEInt(entry.ExternalFileAttributes); + } + else + { + if (entry.IsDirectory) + { // mark entry as directory (from nikolam.AT.perfectinfo.com) + stream.WriteLEInt(16); + } + else + { + stream.WriteLEInt(0); + } + } + + if (entry.Offset >= uint.MaxValue) + { + stream.WriteLEInt(-1); + } + else + { + stream.WriteLEInt((int)entry.Offset); + } + + if (name.Length > 0) + { + stream.Write(name, 0, name.Length); + } + + if (extra.Length > 0) + { + stream.Write(extra, 0, extra.Length); + } + + if (entryComment.Length > 0) + { + stream.Write(entryComment, 0, entryComment.Length); + } + + return ZipConstants.CentralHeaderBaseSize + name.Length + extra.Length + entryComment.Length; + } + + internal static void AddExtraDataAES(ZipEntry entry, ZipExtraData extraData) + { + // Vendor Version: AE-1 IS 1. AE-2 is 2. With AE-2 no CRC is required and 0 is stored. + const int VENDOR_VERSION = 2; + // Vendor ID is the two ASCII characters "AE". + const int VENDOR_ID = 0x4541; //not 6965; + extraData.StartNewEntry(); + // Pack AES extra data field see http://www.winzip.com/aes_info.htm + //extraData.AddLeShort(7); // Data size (currently 7) + extraData.AddLeShort(VENDOR_VERSION); // 2 = AE-2 + extraData.AddLeShort(VENDOR_ID); // "AE" + extraData.AddData(entry.AESEncryptionStrength); // 1 = 128, 2 = 192, 3 = 256 + extraData.AddLeShort((int)entry.CompressionMethod); // The actual compression method used to compress the file + extraData.AddNewEntry(0x9901); + } + + internal static async Task PatchLocalHeaderAsync(Stream stream, ZipEntry entry, + EntryPatchData patchData, CancellationToken ct) + { + var initialPos = stream.Position; + + // Update CRC + stream.Seek(patchData.CrcPatchOffset, SeekOrigin.Begin); + await stream.WriteLEIntAsync((int)entry.Crc, ct); + + // Update Sizes + if (entry.LocalHeaderRequiresZip64) + { + if (patchData.SizePatchOffset == -1) + { + throw new ZipException("Entry requires zip64 but this has been turned off"); + } + // Seek to the Zip64 Extra Data + stream.Seek(patchData.SizePatchOffset, SeekOrigin.Begin); + + // Note: The order of the size fields is reversed when compared to the local header! + await stream.WriteLELongAsync(entry.Size, ct); + await stream.WriteLELongAsync(entry.CompressedSize, ct); + } + else + { + await stream.WriteLEIntAsync((int)entry.CompressedSize, ct); + await stream.WriteLEIntAsync((int)entry.Size, ct); + } + + stream.Seek(initialPos, SeekOrigin.Begin); + } + + internal static void PatchLocalHeaderSync(Stream stream, ZipEntry entry, + EntryPatchData patchData) + { + var initialPos = stream.Position; + stream.Seek(patchData.CrcPatchOffset, SeekOrigin.Begin); + stream.WriteLEInt((int)entry.Crc); + + if (entry.LocalHeaderRequiresZip64) + { + if (patchData.SizePatchOffset == -1) + { + throw new ZipException("Entry requires zip64 but this has been turned off"); + } + + // Seek to the Zip64 Extra Data + stream.Seek(patchData.SizePatchOffset, SeekOrigin.Begin); + + // Note: The order of the size fields is reversed when compared to the local header! + stream.WriteLELong(entry.Size); + stream.WriteLELong(entry.CompressedSize); + } + else + { + stream.WriteLEInt((int)entry.CompressedSize); + stream.WriteLEInt((int)entry.Size); + } + + stream.Seek(initialPos, SeekOrigin.Begin); + } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs b/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs b/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs new file mode 100644 index 000000000000..1b5b0ad530c9 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs @@ -0,0 +1,730 @@ +using ICSharpCode.SharpZipLib.Checksum; +using ICSharpCode.SharpZipLib.Encryption; +using ICSharpCode.SharpZipLib.Zip.Compression; +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; +using System; +using System.IO; + +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// This is an InflaterInputStream that reads the files baseInputStream an zip archive + /// one after another. It has a special method to get the zip entry of + /// the next file. The zip entry contains information about the file name + /// size, compressed size, Crc, etc. + /// It includes support for Stored and Deflated entries. + ///
+ ///
Author of the original java version : Jochen Hoenicke + ///
+ /// + /// This sample shows how to read a zip file + /// + /// using System; + /// using System.Text; + /// using System.IO; + /// + /// using ICSharpCode.SharpZipLib.Zip; + /// + /// class MainClass + /// { + /// public static void Main(string[] args) + /// { + /// using ( ZipInputStream s = new ZipInputStream(File.OpenRead(args[0]))) { + /// + /// ZipEntry theEntry; + /// const int size = 2048; + /// byte[] data = new byte[2048]; + /// + /// while ((theEntry = s.GetNextEntry()) != null) { + /// if ( entry.IsFile ) { + /// Console.Write("Show contents (y/n) ?"); + /// if (Console.ReadLine() == "y") { + /// while (true) { + /// size = s.Read(data, 0, data.Length); + /// if (size > 0) { + /// Console.Write(new ASCIIEncoding().GetString(data, 0, size)); + /// } else { + /// break; + /// } + /// } + /// } + /// } + /// } + /// } + /// } + /// } + /// + /// + public class ZipInputStream : InflaterInputStream + { + #region Instance Fields + + /// + /// Delegate for reading bytes from a stream. + /// + private delegate int ReadDataHandler(byte[] b, int offset, int length); + + /// + /// The current reader this instance. + /// + private ReadDataHandler internalReader; + + private Crc32 crc = new Crc32(); + private ZipEntry entry; + + private long size; + private CompressionMethod method; + private int flags; + private string password; + private readonly StringCodec _stringCodec = ZipStrings.GetStringCodec(); + + #endregion Instance Fields + + #region Constructors + + /// + /// Creates a new Zip input stream, for reading a zip archive. + /// + /// The underlying providing data. + public ZipInputStream(Stream baseInputStream) + : base(baseInputStream, new Inflater(true)) + { + internalReader = new ReadDataHandler(ReadingNotAvailable); + } + + /// + /// Creates a new Zip input stream, for reading a zip archive. + /// + /// The underlying providing data. + /// Size of the buffer. + public ZipInputStream(Stream baseInputStream, int bufferSize) + : base(baseInputStream, new Inflater(true), bufferSize) + { + internalReader = new ReadDataHandler(ReadingNotAvailable); + } + + #endregion Constructors + + /// + /// Optional password used for encryption when non-null + /// + /// A password for all encrypted entries in this + public string Password + { + get + { + return password; + } + set + { + password = value; + } + } + + /// + /// Gets a value indicating if there is a current entry and it can be decompressed + /// + /// + /// The entry can only be decompressed if the library supports the zip features required to extract it. + /// See the ZipEntry Version property for more details. + /// + /// Since uses the local headers for extraction, entries with no compression combined with the + /// flag set, cannot be extracted as the end of the entry data cannot be deduced. + /// + public bool CanDecompressEntry + => entry != null + && IsEntryCompressionMethodSupported(entry) + && entry.CanDecompress + && (!entry.HasFlag(GeneralBitFlags.Descriptor) || entry.CompressionMethod != CompressionMethod.Stored || entry.IsCrypted); + + /// + /// Is the compression method for the specified entry supported? + /// + /// + /// Uses entry.CompressionMethodForHeader so that entries of type WinZipAES will be rejected. + /// + /// the entry to check. + /// true if the compression method is supported, false if not. + private static bool IsEntryCompressionMethodSupported(ZipEntry entry) + { + var entryCompressionMethod = entry.CompressionMethodForHeader; + + return entryCompressionMethod == CompressionMethod.Deflated || + entryCompressionMethod == CompressionMethod.Stored; + } + + /// + /// Advances to the next entry in the archive + /// + /// + /// The next entry in the archive or null if there are no more entries. + /// + /// + /// If the previous entry is still open CloseEntry is called. + /// + /// + /// Input stream is closed + /// + /// + /// Password is not set, password is invalid, compression method is invalid, + /// version required to extract is not supported + /// + public ZipEntry GetNextEntry() + { + if (crc == null) + { + throw new InvalidOperationException("Closed."); + } + + if (entry != null) + { + CloseEntry(); + } + + int header = inputBuffer.ReadLeInt(); + + if (header == ZipConstants.CentralHeaderSignature || + header == ZipConstants.EndOfCentralDirectorySignature || + header == ZipConstants.CentralHeaderDigitalSignature || + header == ZipConstants.ArchiveExtraDataSignature || + header == ZipConstants.Zip64CentralFileHeaderSignature) + { + // No more individual entries exist + Dispose(); + return null; + } + + // -jr- 07-Dec-2003 Ignore spanning temporary signatures if found + // Spanning signature is same as descriptor signature and is untested as yet. + if ((header == ZipConstants.SpanningTempSignature) || (header == ZipConstants.SpanningSignature)) + { + header = inputBuffer.ReadLeInt(); + } + + if (header != ZipConstants.LocalHeaderSignature) + { + throw new ZipException("Wrong Local header signature: 0x" + String.Format("{0:X}", header)); + } + + var versionRequiredToExtract = (short)inputBuffer.ReadLeShort(); + + flags = inputBuffer.ReadLeShort(); + method = (CompressionMethod)inputBuffer.ReadLeShort(); + var dostime = (uint)inputBuffer.ReadLeInt(); + int crc2 = inputBuffer.ReadLeInt(); + csize = inputBuffer.ReadLeInt(); + size = inputBuffer.ReadLeInt(); + int nameLen = inputBuffer.ReadLeShort(); + int extraLen = inputBuffer.ReadLeShort(); + + bool isCrypted = (flags & 1) == 1; + + byte[] buffer = new byte[nameLen]; + inputBuffer.ReadRawBuffer(buffer); + + var entryEncoding = _stringCodec.ZipInputEncoding(flags); + string name = entryEncoding.GetString(buffer); + var unicode = entryEncoding.IsZipUnicode(); + + entry = new ZipEntry(name, versionRequiredToExtract, ZipConstants.VersionMadeBy, method, unicode) + { + Flags = flags, + }; + + if ((flags & 8) == 0) + { + entry.Crc = crc2 & 0xFFFFFFFFL; + entry.Size = size & 0xFFFFFFFFL; + entry.CompressedSize = csize & 0xFFFFFFFFL; + + entry.CryptoCheckValue = (byte)((crc2 >> 24) & 0xff); + } + else + { + // This allows for GNU, WinZip and possibly other archives, the PKZIP spec + // says these values are zero under these circumstances. + if (crc2 != 0) + { + entry.Crc = crc2 & 0xFFFFFFFFL; + } + + if (size != 0) + { + entry.Size = size & 0xFFFFFFFFL; + } + + if (csize != 0) + { + entry.CompressedSize = csize & 0xFFFFFFFFL; + } + + entry.CryptoCheckValue = (byte)((dostime >> 8) & 0xff); + } + + entry.DosTime = dostime; + + // If local header requires Zip64 is true then the extended header should contain + // both values. + + // Handle extra data if present. This can set/alter some fields of the entry. + if (extraLen > 0) + { + byte[] extra = new byte[extraLen]; + inputBuffer.ReadRawBuffer(extra); + entry.ExtraData = extra; + } + + entry.ProcessExtraData(true); + if (entry.CompressedSize >= 0) + { + csize = entry.CompressedSize; + } + + if (entry.Size >= 0) + { + size = entry.Size; + } + + if (method == CompressionMethod.Stored && (!isCrypted && csize != size || (isCrypted && csize - ZipConstants.CryptoHeaderSize != size))) + { + throw new ZipException("Stored, but compressed != uncompressed"); + } + + // Determine how to handle reading of data if this is attempted. + if (IsEntryCompressionMethodSupported(entry)) + { + internalReader = new ReadDataHandler(InitialRead); + } + else + { + internalReader = new ReadDataHandler(ReadingNotSupported); + } + + return entry; + } + + /// + /// Read data descriptor at the end of compressed data. + /// + private void ReadDataDescriptor() + { + if (inputBuffer.ReadLeInt() != ZipConstants.DataDescriptorSignature) + { + throw new ZipException("Data descriptor signature not found"); + } + + entry.Crc = inputBuffer.ReadLeInt() & 0xFFFFFFFFL; + + if (entry.LocalHeaderRequiresZip64) + { + csize = inputBuffer.ReadLeLong(); + size = inputBuffer.ReadLeLong(); + } + else + { + csize = inputBuffer.ReadLeInt(); + size = inputBuffer.ReadLeInt(); + } + entry.CompressedSize = csize; + entry.Size = size; + } + + /// + /// Complete cleanup as the final part of closing. + /// + /// True if the crc value should be tested + private void CompleteCloseEntry(bool testCrc) + { + StopDecrypting(); + + if ((flags & 8) != 0) + { + ReadDataDescriptor(); + } + + size = 0; + + if (testCrc && + ((crc.Value & 0xFFFFFFFFL) != entry.Crc) && (entry.Crc != -1)) + { + throw new ZipException("CRC mismatch"); + } + + crc.Reset(); + + if (method == CompressionMethod.Deflated) + { + inf.Reset(); + } + entry = null; + } + + /// + /// Closes the current zip entry and moves to the next one. + /// + /// + /// The stream is closed + /// + /// + /// The Zip stream ends early + /// + public void CloseEntry() + { + if (crc == null) + { + throw new InvalidOperationException("Closed"); + } + + if (entry == null) + { + return; + } + + if (method == CompressionMethod.Deflated) + { + if ((flags & 8) != 0) + { + // We don't know how much we must skip, read until end. + byte[] tmp = new byte[4096]; + + // Read will close this entry + while (Read(tmp, 0, tmp.Length) > 0) + { + } + return; + } + + csize -= inf.TotalIn; + inputBuffer.Available += inf.RemainingInput; + } + + if ((inputBuffer.Available > csize) && (csize >= 0)) + { + inputBuffer.Available = (int)((long)inputBuffer.Available - csize); + } + else + { + csize -= inputBuffer.Available; + inputBuffer.Available = 0; + while (csize != 0) + { + long skipped = Skip(csize); + + if (skipped <= 0) + { + throw new ZipException("Zip archive ends early."); + } + + csize -= skipped; + } + } + + CompleteCloseEntry(false); + } + + /// + /// Returns 1 if there is an entry available + /// Otherwise returns 0. + /// + public override int Available + { + get + { + return entry != null ? 1 : 0; + } + } + + /// + /// Returns the current size that can be read from the current entry if available + /// + /// Thrown if the entry size is not known. + /// Thrown if no entry is currently available. + public override long Length + { + get + { + if (entry != null) + { + if (entry.Size >= 0) + { + return entry.Size; + } + else + { + throw new ZipException("Length not available for the current entry"); + } + } + else + { + throw new InvalidOperationException("No current entry"); + } + } + } + + /// + /// Reads a byte from the current zip entry. + /// + /// + /// The byte or -1 if end of stream is reached. + /// + public override int ReadByte() + { + byte[] b = new byte[1]; + if (Read(b, 0, 1) <= 0) + { + return -1; + } + return b[0] & 0xff; + } + + /// + /// Handle attempts to read by throwing an . + /// + /// The destination array to store data in. + /// The offset at which data read should be stored. + /// The maximum number of bytes to read. + /// Returns the number of bytes actually read. + private int ReadingNotAvailable(byte[] destination, int offset, int count) + { + throw new InvalidOperationException("Unable to read from this stream"); + } + + /// + /// Handle attempts to read from this entry by throwing an exception + /// + private int ReadingNotSupported(byte[] destination, int offset, int count) + { + throw new ZipException("The compression method for this entry is not supported"); + } + + /// + /// Handle attempts to read from this entry by throwing an exception + /// + private int StoredDescriptorEntry(byte[] destination, int offset, int count) => + throw new StreamUnsupportedException( + "The combination of Stored compression method and Descriptor flag is not possible to read using ZipInputStream"); + + + /// + /// Perform the initial read on an entry which may include + /// reading encryption headers and setting up inflation. + /// + /// The destination to fill with data read. + /// The offset to start reading at. + /// The maximum number of bytes to read. + /// The actual number of bytes read. + private int InitialRead(byte[] destination, int offset, int count) + { + var usesDescriptor = (entry.Flags & (int)GeneralBitFlags.Descriptor) != 0; + + // Handle encryption if required. + if (entry.IsCrypted) + { + if (password == null) + { + throw new ZipException("No password set."); + } + + // Generate and set crypto transform... + var managed = new PkzipClassicManaged(); + byte[] key = PkzipClassic.GenerateKeys(_stringCodec.ZipCryptoEncoding.GetBytes(password)); + + inputBuffer.CryptoTransform = managed.CreateDecryptor(key, null); + + byte[] cryptbuffer = new byte[ZipConstants.CryptoHeaderSize]; + inputBuffer.ReadClearTextBuffer(cryptbuffer, 0, ZipConstants.CryptoHeaderSize); + + if (cryptbuffer[ZipConstants.CryptoHeaderSize - 1] != entry.CryptoCheckValue) + { + throw new ZipException("Invalid password"); + } + + if (csize >= ZipConstants.CryptoHeaderSize) + { + csize -= ZipConstants.CryptoHeaderSize; + } + else if (!usesDescriptor) + { + throw new ZipException($"Entry compressed size {csize} too small for encryption"); + } + } + else + { + inputBuffer.CryptoTransform = null; + } + + if (csize > 0 || usesDescriptor) + { + if (method == CompressionMethod.Deflated && inputBuffer.Available > 0) + { + inputBuffer.SetInflaterInput(inf); + } + + // It's not possible to know how many bytes to read when using "Stored" compression (unless using encryption) + if (!entry.IsCrypted && method == CompressionMethod.Stored && usesDescriptor) + { + internalReader = StoredDescriptorEntry; + return StoredDescriptorEntry(destination, offset, count); + } + + if (!CanDecompressEntry) + { + internalReader = ReadingNotSupported; + return ReadingNotSupported(destination, offset, count); + } + + internalReader = BodyRead; + return BodyRead(destination, offset, count); + } + + + internalReader = ReadingNotAvailable; + return 0; + } + + /// + /// Read a block of bytes from the stream. + /// + /// The destination for the bytes. + /// The index to start storing data. + /// The number of bytes to attempt to read. + /// Returns the number of bytes read. + /// Zero bytes read means end of stream. + public override int Read(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative"); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative"); + } + + if ((buffer.Length - offset) < count) + { + throw new ArgumentException("Invalid offset/count combination"); + } + + return internalReader(buffer, offset, count); + } + + /// + /// Reads a block of bytes from the current zip entry. + /// + /// + /// The number of bytes read (this may be less than the length requested, even before the end of stream), or 0 on end of stream. + /// + /// + /// An i/o error occurred. + /// + /// + /// The deflated stream is corrupted. + /// + /// + /// The stream is not open. + /// + private int BodyRead(byte[] buffer, int offset, int count) + { + if (crc == null) + { + throw new InvalidOperationException("Closed"); + } + + if ((entry == null) || (count <= 0)) + { + return 0; + } + + if (offset + count > buffer.Length) + { + throw new ArgumentException("Offset + count exceeds buffer size"); + } + + bool finished = false; + + switch (method) + { + case CompressionMethod.Deflated: + count = base.Read(buffer, offset, count); + if (count <= 0) + { + if (!inf.IsFinished) + { + throw new ZipException("Inflater not finished!"); + } + inputBuffer.Available = inf.RemainingInput; + + // A csize of -1 is from an unpatched local header + if ((flags & 8) == 0 && + (inf.TotalIn != csize && csize != 0xFFFFFFFF && csize != -1 || inf.TotalOut != size)) + { + throw new ZipException("Size mismatch: " + csize + ";" + size + " <-> " + inf.TotalIn + ";" + inf.TotalOut); + } + inf.Reset(); + finished = true; + } + break; + + case CompressionMethod.Stored: + if ((count > csize) && (csize >= 0)) + { + count = (int)csize; + } + + if (count > 0) + { + count = inputBuffer.ReadClearTextBuffer(buffer, offset, count); + if (count > 0) + { + csize -= count; + size -= count; + } + } + + if (csize == 0) + { + finished = true; + } + else + { + if (count < 0) + { + throw new ZipException("EOF in stored block"); + } + } + break; + } + + if (count > 0) + { + crc.Update(new ArraySegment(buffer, offset, count)); + } + + if (finished) + { + CompleteCloseEntry(true); + } + + return count; + } + + /// + /// Closes the zip input stream + /// + protected override void Dispose(bool disposing) + { + internalReader = new ReadDataHandler(ReadingNotAvailable); + crc = null; + entry = null; + + base.Dispose(disposing); + } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipNameTransform.cs b/ICSharpCode.SharpZipLib/Zip/ZipNameTransform.cs new file mode 100644 index 000000000000..f91b20c3df67 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipNameTransform.cs @@ -0,0 +1,313 @@ +using ICSharpCode.SharpZipLib.Core; +using System; +using System.IO; +using System.Text; + +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// ZipNameTransform transforms names as per the Zip file naming convention. + /// + /// The use of absolute names is supported although its use is not valid + /// according to Zip naming conventions, and should not be used if maximum compatability is desired. + public class ZipNameTransform : INameTransform + { + #region Constructors + + /// + /// Initialize a new instance of + /// + public ZipNameTransform() + { + } + + /// + /// Initialize a new instance of + /// + /// The string to trim from the front of paths if found. + public ZipNameTransform(string trimPrefix) + { + TrimPrefix = trimPrefix; + } + + #endregion Constructors + + /// + /// Static constructor. + /// + static ZipNameTransform() + { + char[] invalidPathChars; + invalidPathChars = Path.GetInvalidPathChars(); + int howMany = invalidPathChars.Length + 2; + + InvalidEntryCharsRelaxed = new char[howMany]; + Array.Copy(invalidPathChars, 0, InvalidEntryCharsRelaxed, 0, invalidPathChars.Length); + InvalidEntryCharsRelaxed[howMany - 1] = '*'; + InvalidEntryCharsRelaxed[howMany - 2] = '?'; + + howMany = invalidPathChars.Length + 4; + InvalidEntryChars = new char[howMany]; + Array.Copy(invalidPathChars, 0, InvalidEntryChars, 0, invalidPathChars.Length); + InvalidEntryChars[howMany - 1] = ':'; + InvalidEntryChars[howMany - 2] = '\\'; + InvalidEntryChars[howMany - 3] = '*'; + InvalidEntryChars[howMany - 4] = '?'; + } + + /// + /// Transform a windows directory name according to the Zip file naming conventions. + /// + /// The directory name to transform. + /// The transformed name. + public string TransformDirectory(string name) + { + name = TransformFile(name); + if (name.Length > 0) + { + if (!name.EndsWith("/", StringComparison.Ordinal)) + { + name += "/"; + } + } + else + { + throw new ZipException("Cannot have an empty directory name"); + } + return name; + } + + /// + /// Transform a windows file name according to the Zip file naming conventions. + /// + /// The file name to transform. + /// The transformed name. + public string TransformFile(string name) + { + if (name != null) + { + string lowerName = name.ToLower(); + if ((trimPrefix_ != null) && (lowerName.IndexOf(trimPrefix_, StringComparison.Ordinal) == 0)) + { + name = name.Substring(trimPrefix_.Length); + } + + name = name.Replace(@"\", "/"); + name = PathUtils.DropPathRoot(name); + + // Drop any leading and trailing slashes. + name = name.Trim('/'); + + // Convert consecutive // characters to / + int index = name.IndexOf("//", StringComparison.Ordinal); + while (index >= 0) + { + name = name.Remove(index, 1); + index = name.IndexOf("//", StringComparison.Ordinal); + } + + name = MakeValidName(name, '_'); + } + else + { + name = string.Empty; + } + return name; + } + + /// + /// Get/set the path prefix to be trimmed from paths if present. + /// + /// The prefix is trimmed before any conversion from + /// a windows path is done. + public string TrimPrefix + { + get { return trimPrefix_; } + set + { + trimPrefix_ = value; + if (trimPrefix_ != null) + { + trimPrefix_ = trimPrefix_.ToLower(); + } + } + } + + /// + /// Force a name to be valid by replacing invalid characters with a fixed value + /// + /// The name to force valid + /// The replacement character to use. + /// Returns a valid name + private static string MakeValidName(string name, char replacement) + { + int index = name.IndexOfAny(InvalidEntryChars); + if (index >= 0) + { + var builder = new StringBuilder(name); + + while (index >= 0) + { + builder[index] = replacement; + + if (index >= name.Length) + { + index = -1; + } + else + { + index = name.IndexOfAny(InvalidEntryChars, index + 1); + } + } + name = builder.ToString(); + } + + if (name.Length > 0xffff) + { + throw new PathTooLongException(); + } + + return name; + } + + /// + /// Test a name to see if it is a valid name for a zip entry. + /// + /// The name to test. + /// If true checking is relaxed about windows file names and absolute paths. + /// Returns true if the name is a valid zip name; false otherwise. + /// Zip path names are actually in Unix format, and should only contain relative paths. + /// This means that any path stored should not contain a drive or + /// device letter, or a leading slash. All slashes should forward slashes '/'. + /// An empty name is valid for a file where the input comes from standard input. + /// A null name is not considered valid. + /// + public static bool IsValidName(string name, bool relaxed) + { + bool result = (name != null); + + if (result) + { + if (relaxed) + { + result = name.IndexOfAny(InvalidEntryCharsRelaxed) < 0; + } + else + { + result = + (name.IndexOfAny(InvalidEntryChars) < 0) && + (name.IndexOf('/') != 0); + } + } + + return result; + } + + /// + /// Test a name to see if it is a valid name for a zip entry. + /// + /// The name to test. + /// Returns true if the name is a valid zip name; false otherwise. + /// Zip path names are actually in unix format, + /// and should only contain relative paths if a path is present. + /// This means that the path stored should not contain a drive or + /// device letter, or a leading slash. All slashes should forward slashes '/'. + /// An empty name is valid where the input comes from standard input. + /// A null name is not considered valid. + /// + public static bool IsValidName(string name) + { + bool result = + (name != null) && + (name.IndexOfAny(InvalidEntryChars) < 0) && + (name.IndexOf('/') != 0) + ; + return result; + } + + #region Instance Fields + + private string trimPrefix_; + + #endregion Instance Fields + + #region Class Fields + + private static readonly char[] InvalidEntryChars; + private static readonly char[] InvalidEntryCharsRelaxed; + + #endregion Class Fields + } + + /// + /// An implementation of INameTransform that transforms entry paths as per the Zip file naming convention. + /// Strips path roots and puts directory separators in the correct format ('/') + /// + public class PathTransformer : INameTransform + { + /// + /// Initialize a new instance of + /// + public PathTransformer() + { + } + + /// + /// Transform a windows directory name according to the Zip file naming conventions. + /// + /// The directory name to transform. + /// The transformed name. + public string TransformDirectory(string name) + { + name = TransformFile(name); + + if (name.Length > 0) + { + if (!name.EndsWith("/", StringComparison.Ordinal)) + { + name += "/"; + } + } + else + { + throw new ZipException("Cannot have an empty directory name"); + } + + return name; + } + + /// + /// Transform a windows file name according to the Zip file naming conventions. + /// + /// The file name to transform. + /// The transformed name. + public string TransformFile(string name) + { + if (name != null) + { + // Put separators in the expected format. + name = name.Replace(@"\", "/"); + + // Remove the path root. + name = PathUtils.DropPathRoot(name); + + // Drop any leading and trailing slashes. + name = name.Trim('/'); + + // Convert consecutive // characters to / + int index = name.IndexOf("//", StringComparison.Ordinal); + while (index >= 0) + { + name = name.Remove(index, 1); + index = name.IndexOf("//", StringComparison.Ordinal); + } + } + else + { + name = string.Empty; + } + + return name; + } + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs b/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs new file mode 100644 index 000000000000..75f1184afcfc --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs @@ -0,0 +1,1004 @@ +using ICSharpCode.SharpZipLib.Checksum; +using ICSharpCode.SharpZipLib.Core; +using ICSharpCode.SharpZipLib.Encryption; +using ICSharpCode.SharpZipLib.Zip.Compression; +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; + +namespace ICSharpCode.SharpZipLib.Zip +{ + /// + /// This is a DeflaterOutputStream that writes the files into a zip + /// archive one after another. It has a special method to start a new + /// zip entry. The zip entries contains information about the file name + /// size, compressed size, CRC, etc. + /// + /// It includes support for Stored and Deflated entries. + /// This class is not thread safe. + ///
+ ///
Author of the original java version : Jochen Hoenicke + ///
+ /// This sample shows how to create a zip file + /// + /// using System; + /// using System.IO; + /// + /// using ICSharpCode.SharpZipLib.Core; + /// using ICSharpCode.SharpZipLib.Zip; + /// + /// class MainClass + /// { + /// public static void Main(string[] args) + /// { + /// string[] filenames = Directory.GetFiles(args[0]); + /// byte[] buffer = new byte[4096]; + /// + /// using ( ZipOutputStream s = new ZipOutputStream(File.Create(args[1])) ) { + /// + /// s.SetLevel(9); // 0 - store only to 9 - means best compression + /// + /// foreach (string file in filenames) { + /// ZipEntry entry = new ZipEntry(file); + /// s.PutNextEntry(entry); + /// + /// using (FileStream fs = File.OpenRead(file)) { + /// StreamUtils.Copy(fs, s, buffer); + /// } + /// } + /// } + /// } + /// } + /// + /// + public class ZipOutputStream : DeflaterOutputStream + { + #region Constructors + + /// + /// Creates a new Zip output stream, writing a zip archive. + /// + /// + /// The output stream to which the archive contents are written. + /// + public ZipOutputStream(Stream baseOutputStream) + : base(baseOutputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true)) + { + } + + /// + /// Creates a new Zip output stream, writing a zip archive. + /// + /// The output stream to which the archive contents are written. + /// Size of the buffer to use. + public ZipOutputStream(Stream baseOutputStream, int bufferSize) + : base(baseOutputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true), bufferSize) + { + } + + internal ZipOutputStream(Stream baseOutputStream, StringCodec stringCodec) : this(baseOutputStream) + { + _stringCodec = stringCodec; + } + + /// + /// + /// + /// + /// + public ZipOutputStream(Stream baseOutputStream, bool existing) + : base(baseOutputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true)) + { + if (existing) + { + using (var zipFile = new ZipFile(baseOutputStream)) + { + zipFile.IsStreamOwner = false; + entries = zipFile.OfType().ToList(); + offset = zipFile.GetCentralDirOffset(); + baseOutputStream_.Seek(offset, SeekOrigin.Begin); + } + } + } + + #endregion Constructors + + /// + /// Gets a flag value of true if the central header has been added for this archive; false if it has not been added. + /// + /// No further entries can be added once this has been done. + public bool IsFinished + { + get + { + return entries == null; + } + } + + /// + /// Set the zip file comment. + /// + /// + /// The comment text for the entire archive. + /// + /// + /// The converted comment is longer than 0xffff bytes. + /// + public void SetComment(string comment) + { + byte[] commentBytes = _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment); + if (commentBytes.Length > 0xffff) + { + throw new ArgumentOutOfRangeException(nameof(comment)); + } + zipComment = commentBytes; + } + + /// + /// Sets the compression level. The new level will be activated + /// immediately. + /// + /// The new compression level (1 to 9). + /// + /// Level specified is not supported. + /// + /// + public void SetLevel(int level) + { + deflater_.SetLevel(level); + defaultCompressionLevel = level; + } + + /// + /// Get the current deflater compression level + /// + /// The current compression level + public int GetLevel() + { + return deflater_.GetLevel(); + } + + /// + /// Get / set a value indicating how Zip64 Extension usage is determined when adding entries. + /// + /// Older archivers may not understand Zip64 extensions. + /// If backwards compatability is an issue be careful when adding entries to an archive. + /// Setting this property to off is workable but less desirable as in those circumstances adding a file + /// larger then 4GB will fail. + public UseZip64 UseZip64 + { + get { return useZip64_; } + set { useZip64_ = value; } + } + + /// + /// Used for transforming the names of entries added by . + /// Defaults to , set to null to disable transforms and use names as supplied. + /// + public INameTransform NameTransform { get; set; } = new PathTransformer(); + + /// + /// Get/set the password used for encryption. + /// + /// When set to null or if the password is empty no encryption is performed + public string Password + { + get + { + return password; + } + set + { + if ((value != null) && (value.Length == 0)) + { + password = null; + } + else + { + password = value; + } + } + } + + /// + /// Write an unsigned short in little endian byte order. + /// + private void WriteLeShort(int value) + { + unchecked + { + baseOutputStream_.WriteByte((byte)(value & 0xff)); + baseOutputStream_.WriteByte((byte)((value >> 8) & 0xff)); + } + } + + /// + /// Write an int in little endian byte order. + /// + private void WriteLeInt(int value) + { + unchecked + { + WriteLeShort(value); + WriteLeShort(value >> 16); + } + } + + /// + /// Write an int in little endian byte order. + /// + private void WriteLeLong(long value) + { + unchecked + { + WriteLeInt((int)value); + WriteLeInt((int)(value >> 32)); + } + } + + // Apply any configured transforms/cleaning to the name of the supplied entry. + private void TransformEntryName(ZipEntry entry) + { + if (NameTransform == null) return; + entry.Name = entry.IsDirectory + ? NameTransform.TransformDirectory(entry.Name) + : NameTransform.TransformFile(entry.Name); + } + + /// + /// Starts a new Zip entry. It automatically closes the previous + /// entry if present. + /// All entry elements bar name are optional, but must be correct if present. + /// If the compression method is stored and the output is not patchable + /// the compression for that entry is automatically changed to deflate level 0 + /// + /// + /// the entry. + /// + /// + /// if entry passed is null. + /// + /// + /// if an I/O error occurred. + /// + /// + /// if stream was finished + /// + /// + /// Too many entries in the Zip file
+ /// Entry name is too long
+ /// Finish has already been called
+ ///
+ /// + /// The Compression method specified for the entry is unsupported. + /// + public void PutNextEntry(ZipEntry entry) + { + if (curEntry != null) + { + CloseEntry(); + } + + PutNextEntry(baseOutputStream_, entry); + + if (entry.IsCrypted) + { + WriteOutput(GetEntryEncryptionHeader(entry)); + } + } + + /// + /// Starts a new passthrough Zip entry. It automatically closes the previous + /// entry if present. + /// Passthrough entry is an entry that is created from compressed data. + /// It is useful to avoid recompression to save CPU resources if compressed data is already disposable. + /// All entry elements bar name, crc, size and compressed size are optional, but must be correct if present. + /// Compression should be set to Deflated. + /// + /// + /// the entry. + /// + /// + /// if entry passed is null. + /// + /// + /// if an I/O error occurred. + /// + /// + /// if stream was finished. + /// + /// + /// Crc is not set
+ /// Size is not set
+ /// CompressedSize is not set
+ /// CompressionMethod is not Deflate
+ /// Too many entries in the Zip file
+ /// Entry name is too long
+ /// Finish has already been called
+ ///
+ /// + /// The Compression method specified for the entry is unsupported
+ /// Entry is encrypted
+ ///
+ public void PutNextPassthroughEntry(ZipEntry entry) + { + if (curEntry != null) + { + CloseEntry(); + } + + if (entry.Crc < 0) + { + throw new ZipException("Crc must be set for passthrough entry"); + } + + if (entry.Size < 0) + { + throw new ZipException("Size must be set for passthrough entry"); + } + + if (entry.CompressedSize < 0) + { + throw new ZipException("CompressedSize must be set for passthrough entry"); + } + + if (entry.CompressionMethod != CompressionMethod.Deflated) + { + throw new NotImplementedException("Only Deflated entries are supported for passthrough"); + } + + if (!string.IsNullOrEmpty(Password)) + { + throw new NotImplementedException("Encrypted passthrough entries are not supported"); + } + + PutNextEntry(baseOutputStream_, entry, 0, true); + } + + + private void WriteOutput(byte[] bytes) + => baseOutputStream_.Write(bytes, 0, bytes.Length); + + private Task WriteOutputAsync(byte[] bytes) + => baseOutputStream_.WriteAsync(bytes, 0, bytes.Length); + + private byte[] GetEntryEncryptionHeader(ZipEntry entry) => + entry.AESKeySize > 0 + ? InitializeAESPassword(entry, Password) + : CreateZipCryptoHeader(entry.Crc < 0 ? entry.DosTime << 16 : entry.Crc); + + internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0, bool passthroughEntry = false) + { + if (entry == null) + { + throw new ArgumentNullException(nameof(entry)); + } + + if (entries == null) + { + throw new InvalidOperationException("ZipOutputStream was finished"); + } + + if (entries.Count == int.MaxValue) + { + throw new ZipException("Too many entries for Zip file"); + } + + CompressionMethod method = entry.CompressionMethod; + + // Check that the compression is one that we support + if (method != CompressionMethod.Deflated && method != CompressionMethod.Stored) + { + throw new NotImplementedException("Compression method not supported"); + } + + // A password must have been set in order to add AES encrypted entries + if (entry.AESKeySize > 0 && string.IsNullOrEmpty(this.Password)) + { + throw new InvalidOperationException("The Password property must be set before AES encrypted entries can be added"); + } + + entryIsPassthrough = passthroughEntry; + + int compressionLevel = defaultCompressionLevel; + + // Clear flags that the library manages internally + entry.Flags &= (int)GeneralBitFlags.UnicodeText; + patchEntryHeader = false; + + bool headerInfoAvailable; + + // No need to compress - definitely no data. + if (entry.Size == 0 && !entryIsPassthrough) + { + entry.CompressedSize = entry.Size; + entry.Crc = 0; + method = CompressionMethod.Stored; + headerInfoAvailable = true; + } + else + { + headerInfoAvailable = (entry.Size >= 0) && entry.HasCrc && entry.CompressedSize >= 0; + + // Switch to deflation if storing isnt possible. + if (method == CompressionMethod.Stored) + { + if (!headerInfoAvailable) + { + if (!CanPatchEntries) + { + // Can't patch entries so storing is not possible. + method = CompressionMethod.Deflated; + compressionLevel = 0; + } + } + else // entry.size must be > 0 + { + entry.CompressedSize = entry.Size; + headerInfoAvailable = entry.HasCrc; + } + } + } + + if (headerInfoAvailable == false) + { + if (CanPatchEntries == false) + { + // Only way to record size and compressed size is to append a data descriptor + // after compressed data. + + // Stored entries of this form have already been converted to deflating. + entry.Flags |= 8; + } + else + { + patchEntryHeader = true; + } + } + + if (Password != null) + { + entry.IsCrypted = true; + if (entry.Crc < 0) + { + // Need to append a data descriptor as the crc isnt available for use + // with encryption, the date is used instead. Setting the flag + // indicates this to the decompressor. + entry.Flags |= 8; + } + } + + entry.Offset = offset; + entry.CompressionMethod = (CompressionMethod)method; + + curMethod = method; + + if ((useZip64_ == UseZip64.On) || ((entry.Size < 0) && (useZip64_ == UseZip64.Dynamic))) + { + entry.ForceZip64(); + } + + // Apply any required transforms to the entry name + TransformEntryName(entry); + + // Write the local file header + offset += ZipFormat.WriteLocalHeader(stream, entry, out var entryPatchData, + headerInfoAvailable, patchEntryHeader, streamOffset, _stringCodec); + + patchData = entryPatchData; + + // Fix offsetOfCentraldir for AES + if (entry.AESKeySize > 0) + offset += entry.AESOverheadSize; + + // Activate the entry. + curEntry = entry; + size = 0; + + if (entryIsPassthrough) + return; + + crc.Reset(); + if (method == CompressionMethod.Deflated) + { + deflater_.Reset(); + deflater_.SetLevel(compressionLevel); + } + } + + /// + /// Starts a new Zip entry. It automatically closes the previous + /// entry if present. + /// All entry elements bar name are optional, but must be correct if present. + /// If the compression method is stored and the output is not patchable + /// the compression for that entry is automatically changed to deflate level 0 + /// + /// + /// the entry. + /// + /// The that can be used to cancel the operation. + /// + /// if entry passed is null. + /// + /// + /// if an I/O error occured. + /// + /// + /// if stream was finished + /// + /// + /// Too many entries in the Zip file
+ /// Entry name is too long
+ /// Finish has already been called
+ ///
+ /// + /// The Compression method specified for the entry is unsupported. + /// + public async Task PutNextEntryAsync(ZipEntry entry, CancellationToken ct = default) + { + if (curEntry != null) await CloseEntryAsync(ct); + await baseOutputStream_.WriteProcToStreamAsync(s => + { + PutNextEntry(s, entry, baseOutputStream_.Position); + }, ct); + + if (!entry.IsCrypted) return; + await WriteOutputAsync(GetEntryEncryptionHeader(entry)); + } + + /// + /// Closes the current entry, updating header and footer information as required + /// + /// + /// Invalid entry field values. + /// + /// + /// An I/O error occurs. + /// + /// + /// No entry is active. + /// + public void CloseEntry() + { + WriteEntryFooter(baseOutputStream_); + + // Patch the header if possible + if (patchEntryHeader) + { + patchEntryHeader = false; + ZipFormat.PatchLocalHeaderSync(baseOutputStream_, curEntry, patchData); + } + + entries.Add(curEntry); + curEntry = null; + } + + /// + public async Task CloseEntryAsync(CancellationToken ct) + { + await baseOutputStream_.WriteProcToStreamAsync(WriteEntryFooter, ct); + + // Patch the header if possible + if (patchEntryHeader) + { + patchEntryHeader = false; + await ZipFormat.PatchLocalHeaderAsync(baseOutputStream_, curEntry, patchData, ct); + } + + entries.Add(curEntry); + curEntry = null; + } + + internal void WriteEntryFooter(Stream stream) + { + if (curEntry == null) + { + throw new InvalidOperationException("No open entry"); + } + + if (entryIsPassthrough) + { + if (curEntry.CompressedSize != size) + { + throw new ZipException($"compressed size was {size}, but {curEntry.CompressedSize} expected"); + } + + offset += size; + return; + } + + long csize = size; + + // First finish the deflater, if appropriate + if (curMethod == CompressionMethod.Deflated) + { + if (size >= 0) + { + base.Finish(); + csize = deflater_.TotalOut; + } + else + { + deflater_.Reset(); + } + } + else if (curMethod == CompressionMethod.Stored) + { + // This is done by Finish() for Deflated entries, but we need to do it + // ourselves for Stored ones + base.GetAuthCodeIfAES(); + } + + // Write the AES Authentication Code (a hash of the compressed and encrypted data) + if (curEntry.AESKeySize > 0) + { + stream.Write(AESAuthCode, 0, 10); + // Always use 0 as CRC for AE-2 format + curEntry.Crc = 0; + } + else + { + if (curEntry.Crc < 0) + { + curEntry.Crc = crc.Value; + } + else if (curEntry.Crc != crc.Value) + { + throw new ZipException($"crc was {crc.Value}, but {curEntry.Crc} was expected"); + } + } + + if (curEntry.Size < 0) + { + curEntry.Size = size; + } + else if (curEntry.Size != size) + { + throw new ZipException($"size was {size}, but {curEntry.Size} was expected"); + } + + if (curEntry.CompressedSize < 0) + { + curEntry.CompressedSize = csize; + } + else if (curEntry.CompressedSize != csize) + { + throw new ZipException($"compressed size was {csize}, but {curEntry.CompressedSize} expected"); + } + + offset += csize; + + if (curEntry.IsCrypted) + { + curEntry.CompressedSize += curEntry.EncryptionOverheadSize; + } + + // Add data descriptor if flagged as required + if ((curEntry.Flags & 8) != 0) + { + stream.WriteLEInt(ZipConstants.DataDescriptorSignature); + stream.WriteLEInt(unchecked((int)curEntry.Crc)); + + if (curEntry.LocalHeaderRequiresZip64) + { + stream.WriteLELong(curEntry.CompressedSize); + stream.WriteLELong(curEntry.Size); + offset += ZipConstants.Zip64DataDescriptorSize; + } + else + { + stream.WriteLEInt((int)curEntry.CompressedSize); + stream.WriteLEInt((int)curEntry.Size); + offset += ZipConstants.DataDescriptorSize; + } + } + } + + + + // File format for AES: + // Size (bytes) Content + // ------------ ------- + // Variable Salt value + // 2 Password verification value + // Variable Encrypted file data + // 10 Authentication code + // + // Value in the "compressed size" fields of the local file header and the central directory entry + // is the total size of all the items listed above. In other words, it is the total size of the + // salt value, password verification value, encrypted data, and authentication code. + + /// + /// Initializes encryption keys based on given password. + /// + protected byte[] InitializeAESPassword(ZipEntry entry, string rawPassword) + { + var salt = new byte[entry.AESSaltLen]; + // Salt needs to be cryptographically random, and unique per file + if (_aesRnd == null) + _aesRnd = RandomNumberGenerator.Create(); + _aesRnd.GetBytes(salt); + int blockSize = entry.AESKeySize / 8; // bits to bytes + + cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true); + + var headBytes = new byte[salt.Length + 2]; + + Array.Copy(salt, headBytes, salt.Length); + Array.Copy(((ZipAESTransform)cryptoTransform_).PwdVerifier, 0, + headBytes, headBytes.Length - 2, 2); + + return headBytes; + } + + private byte[] CreateZipCryptoHeader(long crcValue) + { + offset += ZipConstants.CryptoHeaderSize; + + InitializeZipCryptoPassword(Password); + + byte[] cryptBuffer = new byte[ZipConstants.CryptoHeaderSize]; + using (var rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(cryptBuffer); + } + + cryptBuffer[11] = (byte)(crcValue >> 24); + + EncryptBlock(cryptBuffer, 0, cryptBuffer.Length); + + return cryptBuffer; + } + + /// + /// Initializes encryption keys based on given . + /// + /// The password. + private void InitializeZipCryptoPassword(string password) + { + var pkManaged = new PkzipClassicManaged(); + byte[] key = PkzipClassic.GenerateKeys(ZipCryptoEncoding.GetBytes(password)); + cryptoTransform_ = pkManaged.CreateEncryptor(key, null); + } + + /// + /// Writes the given buffer to the current entry. + /// + /// The buffer containing data to write. + /// The offset of the first byte to write. + /// The number of bytes to write. + /// Archive size is invalid + /// No entry is active. + public override void Write(byte[] buffer, int offset, int count) + { + if (curEntry == null) + { + throw new InvalidOperationException("No open entry."); + } + + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative"); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative"); + } + + if ((buffer.Length - offset) < count) + { + throw new ArgumentException("Invalid offset/count combination"); + } + + if (curEntry.AESKeySize == 0 && !entryIsPassthrough) + { + // Only update CRC if AES is not enabled and entry is not a passthrough one + crc.Update(new ArraySegment(buffer, offset, count)); + } + + size += count; + + if (curMethod == CompressionMethod.Stored || entryIsPassthrough) + { + if (Password != null) + { + CopyAndEncrypt(buffer, offset, count); + } + else + { + baseOutputStream_.Write(buffer, offset, count); + } + } + else + { + base.Write(buffer, offset, count); + } + } + + private void CopyAndEncrypt(byte[] buffer, int offset, int count) + { + const int CopyBufferSize = 4096; + byte[] localBuffer = new byte[CopyBufferSize]; + while (count > 0) + { + int bufferCount = (count < CopyBufferSize) ? count : CopyBufferSize; + + Array.Copy(buffer, offset, localBuffer, 0, bufferCount); + EncryptBlock(localBuffer, 0, bufferCount); + baseOutputStream_.Write(localBuffer, 0, bufferCount); + count -= bufferCount; + offset += bufferCount; + } + } + + /// + /// Finishes the stream. This will write the central directory at the + /// end of the zip file and flush the stream. + /// + /// + /// This is automatically called when the stream is closed. + /// + /// + /// An I/O error occurs. + /// + /// + /// Comment exceeds the maximum length
+ /// Entry name exceeds the maximum length + ///
+ public override void Finish() + { + if (entries == null) + { + return; + } + + if (curEntry != null) + { + CloseEntry(); + } + + long numEntries = entries.Count; + long sizeEntries = 0; + + foreach (var entry in entries) + { + sizeEntries += ZipFormat.WriteEndEntry(baseOutputStream_, entry, _stringCodec); + } + + ZipFormat.WriteEndOfCentralDirectory(baseOutputStream_, numEntries, sizeEntries, offset, zipComment); + + entries = null; + } + + /// > + public override async Task FinishAsync(CancellationToken ct) + { + using (var ms = new MemoryStream()) + { + if (entries == null) + { + return; + } + + if (curEntry != null) + { + await CloseEntryAsync(ct); + } + + long numEntries = entries.Count; + long sizeEntries = 0; + + foreach (var entry in entries) + { + await baseOutputStream_.WriteProcToStreamAsync(ms, s => + { + sizeEntries += ZipFormat.WriteEndEntry(s, entry, _stringCodec); + }, ct); + } + + await baseOutputStream_.WriteProcToStreamAsync(ms, s + => ZipFormat.WriteEndOfCentralDirectory(s, numEntries, sizeEntries, offset, zipComment), + ct); + + entries = null; + } + } + + /// + /// Flushes the stream by calling Flush on the deflater stream unless + /// the current compression method is . Then it flushes the underlying output stream. + /// + public override void Flush() + { + if (curMethod == CompressionMethod.Stored) + { + baseOutputStream_.Flush(); + } + else + { + base.Flush(); + } + } + + #region Instance Fields + + /// + /// The entries for the archive. + /// + private List entries = new List(); + + /// + /// Used to track the crc of data added to entries. + /// + private Crc32 crc = new Crc32(); + + /// + /// The current entry being added. + /// + private ZipEntry curEntry; + + private bool entryIsPassthrough; + + private int defaultCompressionLevel = Deflater.DEFAULT_COMPRESSION; + + private CompressionMethod curMethod = CompressionMethod.Deflated; + + /// + /// Used to track the size of data for an entry during writing. + /// + private long size; + + /// + /// Offset to be recorded for each entry in the central header. + /// + private long offset; + + /// + /// Comment for the entire archive recorded in central header. + /// + private byte[] zipComment = Empty.Array(); + + /// + /// Flag indicating that header patching is required for the current entry. + /// + private bool patchEntryHeader; + + /// + /// The values to patch in the entry local header + /// + private EntryPatchData patchData; + + // Default is dynamic which is not backwards compatible and can cause problems + // with XP's built in compression which cant read Zip64 archives. + // However it does avoid the situation were a large file is added and cannot be completed correctly. + // NOTE: Setting the size for entries before they are added is the best solution! + private UseZip64 useZip64_ = UseZip64.Dynamic; + + /// + /// The password to use when encrypting archive entries. + /// + private string password; + + private readonly StringCodec _stringCodec = ZipStrings.GetStringCodec(); + + #endregion Instance Fields + + #region Static Fields + + // Static to help ensure that multiple files within a zip will get different random salt + private static RandomNumberGenerator _aesRnd = RandomNumberGenerator.Create(); + + #endregion Static Fields + } +} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs b/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs new file mode 100644 index 000000000000..29fa980147e7 --- /dev/null +++ b/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs @@ -0,0 +1,213 @@ +using System; +using System.Text; +using ICSharpCode.SharpZipLib.Core; + +namespace ICSharpCode.SharpZipLib.Zip +{ + internal static class EncodingExtensions + { + public static bool IsZipUnicode(this Encoding e) + => e.Equals(StringCodec.UnicodeZipEncoding); + } + + /// + /// Deprecated way of setting zip encoding provided for backwards compability. + /// Use when possible. + /// + /// + /// If any ZipStrings properties are being modified, it will enter a backwards compatibility mode, mimicking the + /// old behaviour where a single instance was shared between all Zip* instances. + /// + public static class ZipStrings + { + static readonly StringCodec CompatCodec = new StringCodec(); + + private static bool compatibilityMode; + + /// + /// Returns a new instance or the shared backwards compatible instance. + /// + /// + public static StringCodec GetStringCodec() + => compatibilityMode ? CompatCodec : new StringCodec(); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static int CodePage + { + get => CompatCodec.CodePage; + set + { + CompatCodec.CodePage = value; + compatibilityMode = true; + } + } + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static int SystemDefaultCodePage => StringCodec.SystemDefaultCodePage; + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static bool UseUnicode + { + get => !CompatCodec.ForceZipLegacyEncoding; + set + { + CompatCodec.ForceZipLegacyEncoding = !value; + compatibilityMode = true; + } + } + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + private static bool HasUnicodeFlag(int flags) + => ((GeneralBitFlags)flags).HasFlag(GeneralBitFlags.UnicodeText); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static string ConvertToString(byte[] data, int count) + => CompatCodec.ZipOutputEncoding.GetString(data, 0, count); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static string ConvertToString(byte[] data) + => CompatCodec.ZipOutputEncoding.GetString(data); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static string ConvertToStringExt(int flags, byte[] data, int count) + => CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data, 0, count); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static string ConvertToStringExt(int flags, byte[] data) + => CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static byte[] ConvertToArray(string str) + => ConvertToArray(0, str); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static byte[] ConvertToArray(int flags, string str) + => (string.IsNullOrEmpty(str)) + ? Empty.Array() + : CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetBytes(str); + } + + /// + /// Utility class for resolving the encoding used for reading and writing strings + /// + public class StringCodec + { + static StringCodec() + { + try + { + var platformCodepage = Encoding.Default.CodePage; + SystemDefaultCodePage = (platformCodepage == 1 || platformCodepage == 2 || platformCodepage == 3 || platformCodepage == 42) ? FallbackCodePage : platformCodepage; + } + catch + { + SystemDefaultCodePage = FallbackCodePage; + } + + SystemDefaultEncoding = Encoding.GetEncoding(SystemDefaultCodePage); + } + + /// + /// If set, use the encoding set by for zip entries instead of the defaults + /// + public bool ForceZipLegacyEncoding { get; set; } + + /// + /// The default encoding used for ZipCrypto passwords in zip files, set to + /// for greatest compability. + /// + public static Encoding DefaultZipCryptoEncoding => SystemDefaultEncoding; + + /// + /// Returns the encoding for an output . + /// Unless overriden by it returns . + /// + public Encoding ZipOutputEncoding => ZipEncoding(!ForceZipLegacyEncoding); + + /// + /// Returns if is set, otherwise it returns the encoding indicated by + /// + public Encoding ZipEncoding(bool unicode) => unicode ? UnicodeZipEncoding : _legacyEncoding; + + /// + /// Returns the appropriate encoding for an input according to . + /// If overridden by , it always returns the encoding indicated by . + /// + /// + /// + public Encoding ZipInputEncoding(GeneralBitFlags flags) => ZipInputEncoding((int)flags); + + /// + public Encoding ZipInputEncoding(int flags) => ZipEncoding(!ForceZipLegacyEncoding && (flags & (int)GeneralBitFlags.UnicodeText) != 0); + + /// Code page encoding, used for non-unicode strings + /// + /// The original Zip specification (https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT) states + /// that file names should only be encoded with IBM Code Page 437 or UTF-8. + /// In practice, most zip apps use OEM or system encoding (typically cp437 on Windows). + /// Let's be good citizens and default to UTF-8 http://utf8everywhere.org/ + /// + private Encoding _legacyEncoding = SystemDefaultEncoding; + + private Encoding _zipArchiveCommentEncoding; + private Encoding _zipCryptoEncoding; + + /// + /// Returns the UTF-8 code page (65001) used for zip entries with unicode flag set + /// + public static readonly Encoding UnicodeZipEncoding = Encoding.UTF8; + + /// + /// Code page used for non-unicode strings and legacy zip encoding (if is set). + /// Default value is + /// + public int CodePage + { + get => _legacyEncoding.CodePage; + set => _legacyEncoding = (value < 4 || value > 65535 || value == 42) + ? throw new ArgumentOutOfRangeException(nameof(value)) + : Encoding.GetEncoding(value); + } + + private const int FallbackCodePage = 437; + + /// + /// Operating system default codepage, or if it could not be retrieved, the fallback code page IBM 437. + /// + public static int SystemDefaultCodePage { get; } + + /// + /// The system default encoding, based on + /// + public static Encoding SystemDefaultEncoding { get; } + + /// + /// The encoding used for the zip archive comment. Defaults to the encoding for , since + /// no unicode flag can be set for it in the files. + /// + public Encoding ZipArchiveCommentEncoding + { + get => _zipArchiveCommentEncoding ?? _legacyEncoding; + set => _zipArchiveCommentEncoding = value; + } + + /// + /// The encoding used for the ZipCrypto passwords. Defaults to . + /// + public Encoding ZipCryptoEncoding + { + get => _zipCryptoEncoding ?? DefaultZipCryptoEncoding; + set => _zipCryptoEncoding = value; + } + } +} diff --git a/src/Files/Files.csproj b/src/Files/Files.csproj index 109013aa4e2c..1893e114c71d 100644 --- a/src/Files/Files.csproj +++ b/src/Files/Files.csproj @@ -1474,9 +1474,6 @@ 13.0.1 - - 1.3.4 - 2.0.7 @@ -1526,6 +1523,10 @@ {0533133f-2559-4b53-a0fd-0970bc0e312e} Common + + {02ab5173-64af-488b-8d5e-87b8d16ea25c} + ICSharpCode.SharpZipLib + @@ -1548,4 +1549,4 @@ --> - + \ No newline at end of file diff --git a/src/Files/MultilingualResources/Files.af.xlf b/src/Files/MultilingualResources/Files.af.xlf index 9e6d78ec823b..a31f10123d6e 100644 --- a/src/Files/MultilingualResources/Files.af.xlf +++ b/src/Files/MultilingualResources/Files.af.xlf @@ -1967,10 +1967,6 @@ Excluded from sync Uitgesluit van sinchro - - Not calculated - Nie bereken nie - Unknown Onbekende @@ -3375,30 +3371,6 @@ Ons gebruik App Center om tred te hou met programgebruik, foute te vind en ongel Create link in {0} Skep skakel in {0} - - Columns - Kolomme - - - Details - Besonderhede - - - Large Icons - Groot Ikone - - - Medium Icons - Medium Ikone - - - Small Icons - Klein Ikone - - - Tiles - Teëls - Show file extensions Wys lêeruitbreidings @@ -3675,6 +3647,46 @@ Ons gebruik App Center om tred te hou met programgebruik, foute te vind en ongel This setting modifies system files and can have unexpected side effects on your device. The developers take no responsibility in the event an issue occurs as a result. Continuing with this option is an acknowledgment of the risks involved with this action. Please note, uninstalling Files will not undo these changes and may prevent you from opening file explorer unless you turn off this setting before removing Files from your device. Hierdie instelling verander stelsellêers en kan onverwagte newe-effekte op jou toestel hê. Die ontwikkelaars neem geen verantwoordelikheid in die geval dat 'n probleem as gevolg daarvan plaasvind nie. Om voort te gaan met hierdie opsie is 'n erkenning van die risiko's wat by hierdie aksie betrokke is. Let asseblief daarop dat die deïnstalleer van Files nie hierdie veranderinge sal ongedaan maak nie en kan verhoed dat jy File Explorer oopmaak, tensy jy hierdie instelling afskakel voordat jy Files van jou toestel verwyder. + + Not calculated + Not calculated + + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Clear all + Clear all + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.ar.xlf b/src/Files/MultilingualResources/Files.ar.xlf index 484cc3f436ca..419ccbd1739d 100644 --- a/src/Files/MultilingualResources/Files.ar.xlf +++ b/src/Files/MultilingualResources/Files.ar.xlf @@ -3388,30 +3388,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3708,6 +3684,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.ca.xlf b/src/Files/MultilingualResources/Files.ca.xlf index 7b7adc58bfd2..f527e173d631 100644 --- a/src/Files/MultilingualResources/Files.ca.xlf +++ b/src/Files/MultilingualResources/Files.ca.xlf @@ -3366,30 +3366,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Open log location Open log location - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3686,6 +3662,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.cs-CZ.xlf b/src/Files/MultilingualResources/Files.cs-CZ.xlf index 8b4dc9d47bdd..de84a0d13f8a 100644 --- a/src/Files/MultilingualResources/Files.cs-CZ.xlf +++ b/src/Files/MultilingualResources/Files.cs-CZ.xlf @@ -3400,30 +3400,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3720,6 +3696,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.da-DK.xlf b/src/Files/MultilingualResources/Files.da-DK.xlf index 368bca2a2dea..f891d874e7b5 100644 --- a/src/Files/MultilingualResources/Files.da-DK.xlf +++ b/src/Files/MultilingualResources/Files.da-DK.xlf @@ -3391,30 +3391,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3711,6 +3687,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.da.xlf b/src/Files/MultilingualResources/Files.da.xlf index 6242233a2beb..85a760209c87 100644 --- a/src/Files/MultilingualResources/Files.da.xlf +++ b/src/Files/MultilingualResources/Files.da.xlf @@ -3389,30 +3389,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3709,6 +3685,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.de-DE.xlf b/src/Files/MultilingualResources/Files.de-DE.xlf index 3c6e30aa4551..6e1525b0da00 100644 --- a/src/Files/MultilingualResources/Files.de-DE.xlf +++ b/src/Files/MultilingualResources/Files.de-DE.xlf @@ -3362,30 +3362,6 @@ Wir benutzen das App Center, um einen Überblick über die Nutzung von Files zu Create link in {0} Link in {0} erstellen - - Columns - Spalten - - - Details - Details - - - Large Icons - Große Symbole - - - Medium Icons - Mittelgroße Symbole - - - Small Icons - Kleine Symbole - - - Tiles - Kacheln - Show file extensions Dateierweiterungen anzeigen @@ -3682,7 +3658,39 @@ Wir benutzen das App Center, um einen Überblick über die Nutzung von Files zu Clear all Alles löschen + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + - + \ No newline at end of file diff --git a/src/Files/MultilingualResources/Files.el.xlf b/src/Files/MultilingualResources/Files.el.xlf index e1caf6722a66..e853a926528e 100644 --- a/src/Files/MultilingualResources/Files.el.xlf +++ b/src/Files/MultilingualResources/Files.el.xlf @@ -3362,30 +3362,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Δημιουργία συνδέσμου στο {0} - - Columns - Στήλες - - - Details - Λεπτομέρειες - - - Large Icons - Μεγάλα εικονίδια - - - Medium Icons - Μεσαία εικονίδια - - - Small Icons - Μικρά εικονίδια - - - Tiles - Πλακίδια - Show file extensions Εμφάνιση επεκτάσεων αρχείων @@ -3682,6 +3658,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Εκκαθάριση όλων + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.en-GB.xlf b/src/Files/MultilingualResources/Files.en-GB.xlf index 09a40a37d749..5c1269525ae0 100644 --- a/src/Files/MultilingualResources/Files.en-GB.xlf +++ b/src/Files/MultilingualResources/Files.en-GB.xlf @@ -3360,30 +3360,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3680,6 +3656,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.es-419.xlf b/src/Files/MultilingualResources/Files.es-419.xlf index 1ddb51d12a56..7e1a88d7bcb9 100644 --- a/src/Files/MultilingualResources/Files.es-419.xlf +++ b/src/Files/MultilingualResources/Files.es-419.xlf @@ -3363,30 +3363,6 @@ Usamos App Center para realizar un seguimiento del uso de la aplicación, encont Create link in {0} Crear vínculo en {0} - - Columns - Columnas - - - Details - Detalles - - - Large Icons - Iconos grandes - - - Medium Icons - Iconos medianos - - - Small Icons - Iconos pequeños - - - Tiles - Mosaicos - Show file extensions Mostrar extensiones de archivo @@ -3683,6 +3659,38 @@ Usamos App Center para realizar un seguimiento del uso de la aplicación, encont Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.es-ES.xlf b/src/Files/MultilingualResources/Files.es-ES.xlf index 1a1ca191facb..adec28db231c 100644 --- a/src/Files/MultilingualResources/Files.es-ES.xlf +++ b/src/Files/MultilingualResources/Files.es-ES.xlf @@ -3362,30 +3362,6 @@ Utilizamos App Center para hacer un seguimiento del uso de la aplicación, encon Create link in {0} Crear vínculo en {0} - - Columns - Columnas - - - Details - Detalles - - - Large Icons - Iconos grandes - - - Medium Icons - Iconos medianos - - - Small Icons - Iconos pequeños - - - Tiles - Iconos - Show file extensions Mostrar extensiones de archivo @@ -3682,6 +3658,38 @@ Utilizamos App Center para hacer un seguimiento del uso de la aplicación, encon Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.fr-FR.xlf b/src/Files/MultilingualResources/Files.fr-FR.xlf index a47c18ee015c..2fee2279c97a 100644 --- a/src/Files/MultilingualResources/Files.fr-FR.xlf +++ b/src/Files/MultilingualResources/Files.fr-FR.xlf @@ -3361,30 +3361,6 @@ Nous utilisons l'App Center pour suivre l'utilisation de l'application, trouver Create link in {0} Créer un lien dans {0} - - Columns - Colonnes - - - Details - Détails - - - Large Icons - Grandes Icônes - - - Medium Icons - Moyennes Icônes - - - Small Icons - Petites Icônes - - - Tiles - Tuiles - Show file extensions Afficher l'extension des fichiers @@ -3681,6 +3657,38 @@ Nous utilisons l'App Center pour suivre l'utilisation de l'application, trouver Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.he-IL.xlf b/src/Files/MultilingualResources/Files.he-IL.xlf index 6e5dfb61fee8..47bec34b568a 100644 --- a/src/Files/MultilingualResources/Files.he-IL.xlf +++ b/src/Files/MultilingualResources/Files.he-IL.xlf @@ -3374,30 +3374,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3694,6 +3670,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.hi-IN.xlf b/src/Files/MultilingualResources/Files.hi-IN.xlf index 131af7a03458..aa1154cea7d0 100644 --- a/src/Files/MultilingualResources/Files.hi-IN.xlf +++ b/src/Files/MultilingualResources/Files.hi-IN.xlf @@ -3383,30 +3383,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3703,6 +3679,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.hr-HR.xlf b/src/Files/MultilingualResources/Files.hr-HR.xlf index 4c5e533aea90..9c9cd12c8aec 100644 --- a/src/Files/MultilingualResources/Files.hr-HR.xlf +++ b/src/Files/MultilingualResources/Files.hr-HR.xlf @@ -3360,30 +3360,6 @@ Koristimo App Center za praćenje korištenja aplikacija, pronalaženje grešaka Create link in {0} Napravi vezu u {0} - - Columns - Stupci - - - Details - Detalji - - - Large Icons - Velike ikone - - - Medium Icons - Srednje ikone - - - Small Icons - Male ikone - - - Tiles - Pločice - Show file extensions Prikaži ekstenzije datoteka @@ -3680,6 +3656,38 @@ Koristimo App Center za praćenje korištenja aplikacija, pronalaženje grešaka Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.hu-HU.xlf b/src/Files/MultilingualResources/Files.hu-HU.xlf index ea541ed4dc80..5c16b7e51f0b 100644 --- a/src/Files/MultilingualResources/Files.hu-HU.xlf +++ b/src/Files/MultilingualResources/Files.hu-HU.xlf @@ -3362,30 +3362,6 @@ Az App Center szolgáltatásait használjuk az app használatának monitorozás Create link in {0} Parancsikon létrehozása itt: {0} - - Columns - Oszlopok - - - Details - Részletek - - - Large Icons - Nagy ikonok - - - Medium Icons - Közepes ikonok - - - Small Icons - Kis ikonok - - - Tiles - Lista - Show file extensions Fájlkiterjesztések megjelenítése @@ -3682,6 +3658,38 @@ Az App Center szolgáltatásait használjuk az app használatának monitorozás Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.id-ID.xlf b/src/Files/MultilingualResources/Files.id-ID.xlf index 53f7f8af6451..e16e3688729c 100644 --- a/src/Files/MultilingualResources/Files.id-ID.xlf +++ b/src/Files/MultilingualResources/Files.id-ID.xlf @@ -3362,30 +3362,6 @@ Kami menggunakan App Center untuk melacak penggunaan aplikasi, menemukan bug, da Create link in {0} Buat tautan di {0} - - Columns - Kolom - - - Details - Rincian - - - Large Icons - Ikon Besar - - - Medium Icons - Ikon Sedang - - - Small Icons - Ikon Kecil - - - Tiles - Ubin - Show file extensions Tampilkan ekstensi file @@ -3682,6 +3658,38 @@ Kami menggunakan App Center untuk melacak penggunaan aplikasi, menemukan bug, da Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.it-IT.xlf b/src/Files/MultilingualResources/Files.it-IT.xlf index 91c2c2eeb432..8591a3ed9325 100644 --- a/src/Files/MultilingualResources/Files.it-IT.xlf +++ b/src/Files/MultilingualResources/Files.it-IT.xlf @@ -3363,30 +3363,6 @@ Usiamo App Center per tenere traccia dell'utilizzo dell'app, trovare bug e risol Create link in {0} Crea collegamento in {0} - - Columns - Colonne - - - Details - Dettagli - - - Large Icons - Icone Grandi - - - Medium Icons - Icone Medie - - - Small Icons - Icone Piccole - - - Tiles - Riquadri - Show file extensions Mostra estensioni file @@ -3683,6 +3659,38 @@ Usiamo App Center per tenere traccia dell'utilizzo dell'app, trovare bug e risol Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.ja-JP.xlf b/src/Files/MultilingualResources/Files.ja-JP.xlf index 22452122192d..3818e63104bb 100644 --- a/src/Files/MultilingualResources/Files.ja-JP.xlf +++ b/src/Files/MultilingualResources/Files.ja-JP.xlf @@ -3361,30 +3361,6 @@ App Centerを使用して、アプリの使用状況を追跡し、バグを見 Create link in {0} {0} にリンクを作成 - - Columns - - - - Details - 詳細 - - - Large Icons - 大きいアイコン - - - Medium Icons - 中アイコン - - - Small Icons - 小さいアイコン - - - Tiles - タイル - Show file extensions ファイル拡張子を表示 @@ -3681,6 +3657,38 @@ App Centerを使用して、アプリの使用状況を追跡し、バグを見 Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.ka.xlf b/src/Files/MultilingualResources/Files.ka.xlf index 94989ca3a467..dd253d241cab 100644 --- a/src/Files/MultilingualResources/Files.ka.xlf +++ b/src/Files/MultilingualResources/Files.ka.xlf @@ -3362,30 +3362,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} ბმულის შექმნა {0} -ში - - Columns - სვეტები - - - Details - დეტალები - - - Large Icons - მსხვილი ხატულები - - - Medium Icons - საშუალო ხატულები - - - Small Icons - პატარა ხატულები - - - Tiles - ფილები - Show file extensions ფაილის გაფართოებების ჩვენება @@ -3682,6 +3658,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.ko-KR.xlf b/src/Files/MultilingualResources/Files.ko-KR.xlf index 69c2222f439e..9170b43b8bad 100644 --- a/src/Files/MultilingualResources/Files.ko-KR.xlf +++ b/src/Files/MultilingualResources/Files.ko-KR.xlf @@ -3361,30 +3361,6 @@ Files는 어떤 개인정보도 수집, 저장, 공유, 게시하지 않습니 Create link in {0} {0} 폴더로 링크 생성 - - Columns - 열 보기 - - - Details - 자세히 - - - Large Icons - 큰 아이콘 - - - Medium Icons - 중간 아이콘 - - - Small Icons - 작은 아이콘 - - - Tiles - 타일 - Show file extensions 파일 확장자 표시 @@ -3681,6 +3657,38 @@ Files는 어떤 개인정보도 수집, 저장, 공유, 게시하지 않습니 Clear all 모두 비우기 + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.lv-LV.xlf b/src/Files/MultilingualResources/Files.lv-LV.xlf index 1b7e6a4867c5..f34f0859adae 100644 --- a/src/Files/MultilingualResources/Files.lv-LV.xlf +++ b/src/Files/MultilingualResources/Files.lv-LV.xlf @@ -3361,30 +3361,6 @@ Mēs lietojam App Center, lai sekotu līdzi programmas lietojumam, atrastu un no Create link in {0} Izveidot saiti {0} - - Columns - Kolonnas - - - Details - Detaļas - - - Large Icons - Lielas ikonas - - - Medium Icons - Vidēji lielas ikonas - - - Small Icons - Mazas ikonas - - - Tiles - Mozaīka - Show file extensions Rādīt failu paplašinājumus @@ -3681,6 +3657,38 @@ Mēs lietojam App Center, lai sekotu līdzi programmas lietojumam, atrastu un no Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.nl-NL.xlf b/src/Files/MultilingualResources/Files.nl-NL.xlf index ce990aab8238..52d93e996fa9 100644 --- a/src/Files/MultilingualResources/Files.nl-NL.xlf +++ b/src/Files/MultilingualResources/Files.nl-NL.xlf @@ -3384,30 +3384,6 @@ We gebruiken App Center om app-gebruik bij te houden, bugs te vinden en crashes Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3704,6 +3680,38 @@ We gebruiken App Center om app-gebruik bij te houden, bugs te vinden en crashes Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.or-IN.xlf b/src/Files/MultilingualResources/Files.or-IN.xlf index 0e4c499f0740..66fbeba8b75b 100644 --- a/src/Files/MultilingualResources/Files.or-IN.xlf +++ b/src/Files/MultilingualResources/Files.or-IN.xlf @@ -3381,30 +3381,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3701,6 +3677,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.pl-PL.xlf b/src/Files/MultilingualResources/Files.pl-PL.xlf index 099954c12955..25bb14ff5686 100644 --- a/src/Files/MultilingualResources/Files.pl-PL.xlf +++ b/src/Files/MultilingualResources/Files.pl-PL.xlf @@ -3366,30 +3366,6 @@ Korzystamy z App Center, aby śledzić wykorzystanie aplikacji, znajdować błę Create link in {0} Utwórz link w {0} - - Columns - Kolumny - - - Details - Właściwości - - - Large Icons - Duże ikony - - - Medium Icons - Średnie ikony - - - Small Icons - Małe ikony - - - Tiles - Kafelki - Show file extensions Pokaż rozszerzenia plików @@ -3686,6 +3662,38 @@ Korzystamy z App Center, aby śledzić wykorzystanie aplikacji, znajdować błę Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.pt-BR.xlf b/src/Files/MultilingualResources/Files.pt-BR.xlf index 6a000a23c95f..d670bf255540 100644 --- a/src/Files/MultilingualResources/Files.pt-BR.xlf +++ b/src/Files/MultilingualResources/Files.pt-BR.xlf @@ -3361,30 +3361,6 @@ Usamos o App Center para controlar o uso do aplicativo, encontrar bugs e corrigi Create link in {0} Criar link em {0} - - Columns - Colunas - - - Details - Detalhes - - - Large Icons - Ícones Grandes - - - Medium Icons - Ícones Médios - - - Small Icons - Ícones Pequenos - - - Tiles - Blocos - Show file extensions Mostrar extensões de arquivo @@ -3681,6 +3657,38 @@ Usamos o App Center para controlar o uso do aplicativo, encontrar bugs e corrigi Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.pt-PT.xlf b/src/Files/MultilingualResources/Files.pt-PT.xlf index 2ffb53a11e5d..e024bb84d1a0 100644 --- a/src/Files/MultilingualResources/Files.pt-PT.xlf +++ b/src/Files/MultilingualResources/Files.pt-PT.xlf @@ -3364,30 +3364,6 @@ Nós utilizamos o App Center para acompanhar a utilização da aplicação, enco Create link in {0} Criar ligação em {0} - - Columns - Colunas - - - Details - Detalhes - - - Large Icons - Ícones Grandes - - - Medium Icons - Ícones Médios - - - Small Icons - Ícones Pequenos - - - Tiles - Mosaicos - Show file extensions Mostrar extensões dos ficheiros @@ -3684,7 +3660,39 @@ Nós utilizamos o App Center para acompanhar a utilização da aplicação, enco Clear all Limpar tudo + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + - + \ No newline at end of file diff --git a/src/Files/MultilingualResources/Files.ru-RU.xlf b/src/Files/MultilingualResources/Files.ru-RU.xlf index 5be3b1f409f2..077eedcca06f 100644 --- a/src/Files/MultilingualResources/Files.ru-RU.xlf +++ b/src/Files/MultilingualResources/Files.ru-RU.xlf @@ -3368,30 +3368,6 @@ Files не собирает, хранит, распространяет или Create link in {0} Создать ссылку в {0} - - Columns - Столбцы - - - Details - Таблица - - - Large Icons - Огромные значки - - - Medium Icons - Крупные значки - - - Small Icons - Обычные значки - - - Tiles - Плитка - Show file extensions Показать расширения файлов @@ -3684,6 +3660,38 @@ Files не собирает, хранит, распространяет или Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.sv-SE.xlf b/src/Files/MultilingualResources/Files.sv-SE.xlf index 5ddca04665d6..4ef907acb668 100644 --- a/src/Files/MultilingualResources/Files.sv-SE.xlf +++ b/src/Files/MultilingualResources/Files.sv-SE.xlf @@ -3392,30 +3392,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3712,6 +3688,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.ta.xlf b/src/Files/MultilingualResources/Files.ta.xlf index e30f16053ce9..e01e02785b88 100644 --- a/src/Files/MultilingualResources/Files.ta.xlf +++ b/src/Files/MultilingualResources/Files.ta.xlf @@ -3362,30 +3362,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3682,6 +3658,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.tr-TR.xlf b/src/Files/MultilingualResources/Files.tr-TR.xlf index 75d6c944a427..f500540df254 100644 --- a/src/Files/MultilingualResources/Files.tr-TR.xlf +++ b/src/Files/MultilingualResources/Files.tr-TR.xlf @@ -3381,30 +3381,6 @@ Uygulama kullanımını takip etmek, hataları bulmak ve çökmeleri düzeltmek Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3701,6 +3677,38 @@ Uygulama kullanımını takip etmek, hataları bulmak ve çökmeleri düzeltmek Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.uk-UA.xlf b/src/Files/MultilingualResources/Files.uk-UA.xlf index 4aa26dd725ea..373a2079111b 100644 --- a/src/Files/MultilingualResources/Files.uk-UA.xlf +++ b/src/Files/MultilingualResources/Files.uk-UA.xlf @@ -3389,30 +3389,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3709,6 +3685,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.vi.xlf b/src/Files/MultilingualResources/Files.vi.xlf index ada12c447d4a..3ba362b64aa2 100644 --- a/src/Files/MultilingualResources/Files.vi.xlf +++ b/src/Files/MultilingualResources/Files.vi.xlf @@ -3381,30 +3381,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} Create link in {0} - - Columns - Columns - - - Details - Details - - - Large Icons - Large Icons - - - Medium Icons - Medium Icons - - - Small Icons - Small Icons - - - Tiles - Tiles - Show file extensions Show file extensions @@ -3701,6 +3677,38 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.zh-Hans.xlf b/src/Files/MultilingualResources/Files.zh-Hans.xlf index db8a601fed10..47913af8f826 100644 --- a/src/Files/MultilingualResources/Files.zh-Hans.xlf +++ b/src/Files/MultilingualResources/Files.zh-Hans.xlf @@ -3364,30 +3364,6 @@ Files 不收集、存储、共享或发布任何个人信息。 Create link in {0} 在 {0} 处创建链接 - - Columns - 列视图 - - - Details - 详细信息 - - - Large Icons - 网格视图(大) - - - Medium Icons - 网格视图(中) - - - Small Icons - 网格视图(小) - - - Tiles - 平铺视图 - Show file extensions 显示文件扩展名 @@ -3684,6 +3660,38 @@ Files 不收集、存储、共享或发布任何个人信息。 Clear all 清空 + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/MultilingualResources/Files.zh-Hant.xlf b/src/Files/MultilingualResources/Files.zh-Hant.xlf index 26c86e13185e..cec1af567884 100644 --- a/src/Files/MultilingualResources/Files.zh-Hant.xlf +++ b/src/Files/MultilingualResources/Files.zh-Hant.xlf @@ -3361,30 +3361,6 @@ Files 不會收集、存儲、共享或發布任何使用者的個資。 Create link in {0} 建立捷徑至 {0} - - Columns - 清單 - - - Details - 詳細資料 - - - Large Icons - 大圖示 - - - Medium Icons - 中圖示 - - - Small Icons - 小圖示 - - - Tiles - 並排 - Show file extensions 顯示檔案副檔名 @@ -3681,6 +3657,38 @@ Files 不會收集、存儲、共享或發布任何使用者的個資。 Clear all Clear all + + Columns + Columns + + + Large Icons + Large Icons + + + Medium Icons + Medium Icons + + + Small Icons + Small Icons + + + Tiles + Tiles + + + Create Library + Create Library + + + Enter Library name + Enter Library name + + + Show folder size + Show folder size + diff --git a/src/Files/Strings/af/Resources.resw b/src/Files/Strings/af/Resources.resw index a3df4f013d35..1775aacd8dbe 100644 --- a/src/Files/Strings/af/Resources.resw +++ b/src/Files/Strings/af/Resources.resw @@ -12,4 +12,2731 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Maak in nuwe duimgids oop + + + Maak in nuwe venster oop + + + Nuwe venster + + + Omslag + + + Bispatroon-beeld + + + Teksdokument + + + Kopieer ligging + + + Plak + + + Blaai + + + Skywe + + + Naam + + + Datum verander + + + Soort: + + + Grootte + + + Maak oop in Terminal... + + + Geskep: + + + Item Naam + + + Plek: + + + Grootte: + + + Hierdie aksie kan nie gedoen word nie + + + Die bestemmingsomslag + + + is 'n subgids van die brongids + + + Slaan oor + + + Itemsoort: + + + Selekteer Alles + + + Keer Seleksie Om + + + Maak Seleksie Skoon + + + Besonderhede + + + Gemodifiseer: + + + Oopgemaak: + + + Maak alle items skoon + + + Tik 'n pad in + + + Soek + + + Lêers wat jy voorheen verkry het sal hier verskyn + + + Maak lêerligging oop + + + Verwyder hierdie item + + + Onlangse lêers + + + Nuttige skakels + + + Kopieer + + + Dien terugvoer in + + + Oor + + + Datumformaat + + + Kies jou kleur + + + Wys uitbreidings vir bekende lêersoorte + + + Wys versteekte lêers en omslae + + + Eksperimentele + + + Voorkoms + + + Eksperimentele + + + Voorkeure + + + WAARSKUWING: EKSPERIMENTELE KENMERKE WAT VOORLÊ! + + + Gaan voort waar jy opgehou het + + + Maak ’n nuwe duimgids oop + + + Maak 'n spesifieke bladsy of bladsye oop + + + Voorkeure + + + Weskskerm + + + Dokumente + + + Aflaaie + + + Tuis + + + Musiek + + + Prente + + + Onpen van gunstelinge + + + Video's + + + Sorteer volgens + + + Naam + + + Datum gewysig + + + Soort + + + Grootte + + + Stygende + + + Dalende + + + Verfris + + + Plak + + + Maak oop in Terminal... + + + MD5Hash: + + + Nuwe + + + Omslag + + + Lêer + + + Eienskappe + + + Eienskappe + + + Uitreksel + + + Maak oop + + + Maak oop in nuwe duimgids + + + Maak oop in nuwe venster + + + Stel as werkskermagtergrond + + + Deel + + + Knip + + + Kopieer + + + Skrap + + + Hernoem + + + Pen na Gunstelinge + + + Eienskappe + + + Welkom by Files! + + + Verleen Toestemming + + + Om te begin, moet jy ons toestemming gee om jou lêers te vertoon. Dit sal 'n Instellingsbladsy oopmaak waar jy ons hierdie toestemming kan gee. Jy sal die program moet heropen sodra jy hierdie stap voltooi het. + + + Lêer omslag + + + Lêer + + + Tik 'n itemnaam in + + + Tik 'n itemnaam in + + + Stel Naam Op + + + Verstek + + + Lig + + + Donker + + + Toepassing + + + Stelsel + + + Nuwe Omslag + + + Nuwe Lêer + + + Hierdie omslag is leeg. + + + Terug (Alt+Linkerpyltjie) + + + Vorentoe (Alt+Regterpyltjie) + + + Op (Alt+Oppyltjie) + + + Verfris (F5) + + + Soek (Ctrl+F) + + + Eienskappe + + + Eienskappe: + + + Eienskappe + + + 'n Item met hierdie naam bestaan reeds in hierdie gids. + + + Vervang bestaande item + + + Item bestaan reeds + + + Stel as sluitskermagtergrond + + + Groot Ikone + + + Medium Ikone + + + Klein Ikone + + + Teëls + + + OK + + + Ons kon nie hierdie item skrap nie + + + Voeg asseblief die nodige aandrywer in om toegang tot hierdie item te verkry. + + + Hardeskyf Ongeprop + + + Die lêer wat jy probeer toegang verkry is dalk verskuif of geskrap. + + + Lêer Nie Gevind Nie + + + Die lêer wat jy probeer voorskou, is dalk verskuif of geskrap. + + + Die omslag wat jy probeer toegang verkry, is dalk verskuif of geskrap. + + + Het jy hierdie omslag geskrap? + + + Die lêer wat jy probeer skrap word tans deur 'n ander toepassing gebruik. + + + Lêer is in gebruik + + + Probeer weer + + + OK + + + Uitleg + + + Keuringsopsies + + + Leesalleen + + + Versteekte + + + Maak herwin bin leeg + + + Herstel + + + {0} dae gelede + + + {0} dag gelede + + + {0} ure gelede + + + {0} uur gelede + + + {0} minute gelede + + + {0} minuut gelede + + + {0} sekondes gelede + + + Nuwe Duimgids + + + Die aangevraagde bewerking word nie ondersteun nie + + + {0} vry van {1} + + + Maak Oop Met + + + Die itemnaam moet nie die volgende karakters bevat nie: \ / : * ? " < > | + + + OK + + + Die itemnaam moet nie die volgende karakters bevat nie: \ / : * ? " < > | + + + item gekies + + + items gekies + + + item + + + items + + + Skrap tans lêers + + + Plak lêers + + + Skuif lêers na die Herwin Bin + + + Ja + + + Is jy seker jy wil al hierdie items permanent skrap? + + + Maak herwin bin leeg + + + grepe + + + KB + + + MB + + + GB + + + TB + + + PB + + + B + + + Laat loop as administrateur + + + Ontsluit + + + Tik wagwoord in om die aandrywer te ontsluit + + + Wagwoord + + + Kontroleer asseblief die wagwoord en probeer weer. + + + BitLocker fout + + + Bevat: + + + {0:#,##0} lêers, {1:#,##0} omslae + + + {0:#,##0} lêers, {1:#,##0} omslae van {2:#,##0} plekke + + + Hardloop as 'n ander gebruiker + + + Alle soorte {0} + + + Verskillende soorte + + + Alles in {0} + + + Gebruikte spasie: + + + Gratis spasie: + + + Kapasiteit: + + + Lêerstelsel: + + + Onlangse + + + Skuif duimgids hier + + + Status + + + Voeg 'n nuwe bladsy by + + + Bladsye: + + + Windows-verstek + + + Geen resultate + + + Kan nie toegang verkry tot enige items om te vertoon nie + + + Skuif na {0} + + + Die bron- en bestemminglêername is dieselfde. + + + Die bestemmingomslag is dieselfde as die bronomslag. + + + kopieer na {0} + + + Skep kortpad + + + Maak lêerligging oop + + + Argumente: + + + Bestemming: + + + Kortpadsoort: + + + Werk gids: + + + Toepassing + + + Lêer + + + Omslag + + + Webskakel + + + Algemeen + + + Kortpad + + + Kortpad + + + Internet kortpad + + + {0} - kortpad + + + Maak lêerligging oop + + + Onbekende + + + Maak logligging oop + + + Files het in 'n probleem gehardloop waarvoor die ontwikkelaars nog nie voorberei het nie. + + + Iets het verkeerd gegaan! + + + Rapporteer hierdie probleem + + + Medewerkers + + + Vrystelling notas + + + Die item wat verwys word, is óf ongeldig óf ontoeganklik. {0} Foutboodskap: {0}{1} + + + Ongeldige item + + + Weergawe: + + + Daar is niks om nou te deel nie... + + + Die items wat jy gekies het sal gedeel word + + + Die geselekteerde item sal gedeel word + + + Deel {0} + + + Deel {0} {1} + + + Die item wat jy probeer hernoem bestaan nie meer nie. Verifieer asseblief die korrekte ligging van die item wat jy moet hernoem. + + + Item bestaan nie meer nie + + + Die naam wat gespesifiseer is was ongeldig. Kontroleer asseblief die gewenste itemnaam en probeer weer. + + + Ongeldige itemnaam + + + Die lengte van die itemnaam wat gespesifiseer word oorskry die maksimum limiet. Kontroleer asseblief die gewenste naam en probeer weer. + + + Item naam was te lank + + + Meer + + + Nee + + + Ja + + + Die toepassing moet herbegin word om hierdie instellings toe te pas, wil jy die program herbegin? + + + Ondersteun ons + + + Ons kon nie hierdie item skep nie + + + Toegang Geweier + + + Voeg duimgids by + + + Maak duimgidse oop + + + Multitasking + + + Multi-taak + + + Stel as + + + Kopieer ligging + + + Kanselleer + + + Skep 'n Nuwe Item + + + Kies 'n tipe vir hierdie nuwe item hieronder + + + Lêer + + + Skep 'n leë lêer + + + Omslag + + + Skep 'n leë omslag + + + Uitlegmodus + + + Navigeer agteruit + + + Navigeer vorentoe + + + Verfris die gids + + + Gaan op een gids + + + Vertikale duimgids vlieg + + + Vertikale duimgids vlieg + + + Redigeer terminaaltoepassings + + + Redigeer terminaaltoepassings + + + Taal + + + Terminaaltoepassings + + + Redigeer terminaaltoepassings + + + Skuif oorloopitems in 'n subkieslys + + + Pen Herwin Bin na gunstelinge + + + Wys 'n bevestigingsdialoog wanneer lêers of omslae geskrap word + + + Daar was 'n probleem met die redding van 'n paar eienskappe. + + + Fout + + + Probeer weer + + + Sluit in elk geval + + + Gradering + + + Item Pad + + + Item Soort + + + + Titel + + + Vak + + + Kommentaar + + + Kopiereg + + + Datum Gewysig + + + Bit Diepte + + + Dimensies + + + Horisontale Grootte + + + Vertikale Grootte + + + Horisontale Resolusie + + + Vertikale Resolusie + + + Kompressie + + + Kleur spasie + + + sRGB + + + Ongespesifiseerde + + + Lengtegraad Desimale + + + Breedtegraad Desimale + + + Breedtegraad + + + Lengtegraad + + + Breedtegraad Verw + + + Lengtegraad Verw + + + Hoogte + + + Datum Geneem + + + Kamera Vervaardiger + + + Kamera Model + + + Blootstelling Tyd + + + Fokuslengte + + + Aperture + + + Mense Name + + + Kanaal Telling + + + Formaat + + + Monster koers + + + Kunstenaar + + + Album Kunstenaar + + + Album Titel + + + Kunstenaar + + + Slae Per Minuut + + + Komponis + + + Geleier + + + Skyfnommer + + + Genre + + + Snit Nommer + + + Duur + + + Raam Telling + + + Beskermingsoort + + + Outeur Url + + + Inhoud Verspreider + + + Datum Vrygestel + + + Reeksnaam + + + Seisoen Nommer + + + Episode Nommer + + + Produsent + + + Promosie-url + + + Verskaffer Styl + + + Uitgewer + + + Duimnael Groot Pad + + + Duimnael Groot Uri + + + Duimnael Klein Pad + + + Duimnael Klein Uri + + + Gebruiker Web Url + + + Skrywer + + + Jaar + + + Bydraer + + + Laaste Skrywer + + + Hersiening Nommer + + + Weergawe + + + Datum Geskep + + + Datum Gestoor + + + Datum Gedruk + + + Totale Redigering Tyd + + + Patroonplaat + + + Woordtelling + + + Karaktertelling + + + Lyn Telling + + + Paragraaf Telling + + + Bladsy Telling + + + Skyfietelling + + + Raam Koers + + + Enkodering Bitrate + + + Kompressie + + + Raam Breedte + + + Raam Hoogte + + + Oriëntasie + + + Kern + + + Beeld + + + Foto + + + GPS + + + Media + + + Oudio + + + Musiek + + + Video + + + Dokument + + + Adres + + + Keuringsopsies + + + Kies + + + Maak lêers oop met 'n enkele klik + + + Lêer + + + Omslag + + + Herwin bin item + + + Kortpad item + + + Skywe + + + Beweeg hier + + + Veilig om hardeware te verwyder + + + Die toestel kan nou veilig van die rekenaar verwyder word. + + + Probleem Uitskiet Toestel + + + Hierdie toestel word tans gebruik. Sluit enige programme, vensters of duimgidse wat die toestel kan gebruik, en probeer dan weer. + + + Ontkoppel + + + Dupliseer duimgids + + + Skuif duimgids na nuwe venster + + + Nuwe duimgids + + + Nuwe duimgids (Ctrl+T) + + + Nuwe duimgids (Ctrl+T) + + + Maak duimgids toe (Ctrl+W) + + + Sommige eienskappe kan persoonlike inligting bevat. + + + Maak Skoon + + + Maak Alle Eienskappe Skoon + + + Kontroleer die status van lêerbedrywighede hier + + + Status Sentrum + + + Soek + + + Soekresultate in {1} vir {0} + + + Soekresultate + + + Kyk wie het bygedra tot Files + + + Vind uit wat nuut is in Files + + + Stuur die ontwikkelaars 'n probleemverslag met meer inligting + + + Ondersteun ons op PayPal + + + Lys en sorteer gidse saam met lêers + + + Besonderhede + + + Nee + + + Wil jy die nuutste weergawe van Files aflaai en installeer? + + + Bywerkings Beskikbaar + + + Versteek beskermde bedryfstelsellêers (Aanbeveel) + + + Wys ongedekseerde items wanneer jy na lêers en vouers soek + + + Stel individuele voorkeure vir individuele gidse in staat + + + Pasmaak die regsklikkontekskieslys + + + Oorspronklike pad + + + Oorspronklike pad + + + Voeg By + + + Redigeer + + + Verwyder + + + Maak altyd nuwe oortjies in dubbele paneelmodus oop + + + Aktiveer dubbele paneelaansig + + + Maak oop in sekondêre paneel + + + Nuwe paneel + + + Maak oop in sekondêre paneel + + + Dubbele paneel + + + Omslae + + + Besonderhede (Ctrl+Shift+1) + + + Besonderhede + + + Uitleg + + + Uitleg + + + Uitleg + + + Voorskou + + + Groot Ikone + + + Groot Ikone (Ctrl+Shift+5) + + + Medium Ikone + + + Medium Ikone (Ctrl+Shift+4) + + + Klein Ikone + + + Klein Ikone (Ctrl+Shift+3) + + + Teëls + + + Teëls (Ctrl+Shift+2) + + + Wys lêeruitbreidings + + + Wys versteekte items + + + Datum geskrap + + + Datum geskrap + + + Wolkaandrywers + + + Meer opsies... + + + Bevestig + + + Voer Bondels Uit + + + Voer Bondels In + + + Bundel met dieselfde naam bestaan reeds! + + + Insetveld kan nie leeg wees nie! + + + Skep Bondel + + + Voer Bondelnaam in + + + Voer Bondelnaam in + + + Maak oop in nuwe duimgids + + + Maak itemligging oop + + + Verwyder van bondel + + + Verwyder van bondel + + + Bondels + + + Sleep en laat val enige lêer of vouer hier om vinnig toegang daartoe te verkry + + + Bevestig + + + Voer Bondelnaam in + + + Gewenste naam + + + Bevestig + + + Tik nuwe naam in + + + Hernoem "{0}" + + + Skep Bondel + + + Bevestig + + + Voeg Bondel By + + + Skep Bondel + + + Hernoem Bondel + + + Gewenste naam + + + Wissel die voorskoupaneel (Ctrl+P) + + + Geen voorskou beskikbaar nie + + + Geen besonderhede beskikbaar nie + + + Item Naam + + + Onpen van Gunstelinge + + + Netwerkaandrywers + + + Netwerk + + + Lêerbesonderhede + + + Lêervoorskou + + + Geselekteerde lêervoorskoupaneel + + + Dokumentasie + + + Besoek die Files dokumentasie webwerf + + + Deurlopende take vlieg uit + + + Beskikbaar wanneer aanlyn + + + Beskikbaar vanlyn + + + Gedeeltelik beskikbaar vanlyn + + + Sinkroniseer + + + Uitgesluit van sinchro + + + Onbekende + + + Multi-kies + + + As jy 'n lêeruitbreiding verander, kan die lêer onbruikbare word. Is jy seker jy wil dit verander? + + + CD ROM-aandrywer + + + Wolkaandrywer + + + Vaste Skyfaandrywer + + + Sagte Skyfaandrywer + + + Netwerkaandrywer + + + Onthefde Aandrywer + + + RAM Skyfaandrywer + + + Verwyderbare Bergingstoestel + + + Onbekende + + + Virtuele Aandrywer + + + Datum geskep + + + Datum geskep + + + Meer opsies... + + + Karteer netwerkaandrywer + + + Ontkoppel + + + Meer bondels opsies + + + Meer dryf opsies + + + Gunstelinge + + + Biblioteke + + + Geen item gekies nie + + + Teiken + + + Argumente + + + Maak oop in nuwe Paneel + + + Wys biblioteekafdeling + + + Verstek + + + Pasgemaakte temas + + + Item telling + + + Sluit duimgidse aan die regterkant + + + Skrapping het Misluk + + + 'n Onbekende fout het voorgekom. + + + Skrapping voltooi + + + Skrapping gekanselleer + + + Die operasie is voltooi. + + + Herwin voltooi + + + Skuif voltooi + + + Kopieer voltooi + + + Nee + + + Hierdie aksie vereis administrateurregte + + + Wil jy graag as administrateur voortgaan? + + + Files loop as administrateur + + + Administrateur + + + Kolomme + + + Kolomme (Ctrl+Shift+6) + + + Pen na die Beginkieslys + + + Onpen van die Beginkieslys + + + Kolomme + + + Biblioteek + + + Biblioteek + + + Biblioteek + + + Plekke: + + + Voeg By + + + Verstek + + + Stel as verstek stoorpad + + + Verwyder + + + Geen liggings nie + + + Insetveld kan nie leeg wees nie! + + + Die naam mag nie die volgende karakters bevat nie: \ / : * ? " < > | + + + Die gebruik van hierdie naam word nie toegelaat nie! + + + Biblioteek met dieselfde naam bestaan reeds! + + + Skrap Biblioteek + + + Skep + + + Maak Storage Sense Oop + + + Maak Storage Sense Oop + + + Kopieer + + + {0} items sal gekopieer word + + + Kopieer item(s) + + + Skrap item(s) + + + Skuif + + + {0} items sal verskuif word + + + Skuif item(s) + + + {0} items sal geskrap word + + + Voortgaan + + + Daar is {0} teenstrydige lêername, en {1} uitgaande item(s). + + + Teenstrydige lêernaam(s) + + + Daar is een teenstrydige lêernaam, en {0} uitgaande item(s). + + + Een item sal gekopieer word + + + Een item sal geskrap word + + + Een item sal verskuif word + + + Skrap permanent + + + ISO spoed + + + Maak omslae in nuwe duimgids oop + + + Wys Bondels-hulptuig + + + Wys Aandrywers-hulptuig + + + Wys Omslae-hulptuig + + + Wys onlangse lêers hulptuig + + + Bestuur Hulptuie + + + Bestuur hulptuie + + + Het jy nie gevind waarna jy soek nie? + + + Soek ongedekseerde items. + + + Bestuur Hulptuie + + + Maak temasomslag oop + + + Vervang bestaande + + + Slaan oor + + + Vervang bestaande + + + Slaan oor + + + Vervang bestaande + + + Slaan oor + + + Genereer nuwe naam + + + Ontdoen + + + Daar is {0} teenstrydige lêername. + + + Daar is een teenstrydige lêernaam. + + + Skep omslag met seleksie + + + Naam: + + + Soort: + + + Datum gewysig: + + + Maak oop met + + + Lêerikoon + + + Oorspronklike pad kolom + + + Item soort kolom + + + Datum gewysigde kolom + + + Datum geskrap kolom + + + Kantbalk herskaleerder + + + Sorteer + + + Sorteer + + + Sorteer + + + Groep Deur + + + Geen + + + Naam + + + Datum gewysig + + + Datum geskep + + + Grootte + + + Datum geskrap + + + Oorspronklike omslag + + + Soort + + + Sorteer Volgens + + + Sorteer rigting + + + Stygende + + + Dalende + + + Onsettend groot + + + Baie groot + + + Groot + + + Medium + + + Klein + + + Baie klein + + + Vandag + + + Gister + + + Vroeër hierdie week + + + Verlede week + + + Hierdie maand + + + Verlede maand + + + Hierdie jaar + + + Ouer + + + Voor {0} + + + {0} item + + + {0} items + + + Heropen geslote duimgids + + + Privaatheidsbeleid + + + Bekyk Files se privaatheidsbeleid + + + Hernoem + + + Skuif gekanselleer + + + Kopieer gekanselleer + + + Die verskuiwing van {0} item van {1} na {2} is gekanselleer + + + Die verskuiwing van {0} items van {1} na {2} is gekanselleer nadat {3} items verskuif is + + + Die verskuiwing van {0} items van {1} na {2} is gekanselleer nadat {3} item verskuif is + + + Skuif {0} item van {1} na {2} + + + Skuif {0} items van {1} na {2} + + + Suksesvol verskuif {0} item van {1} na {2} + + + Suksesvol verskuif {0} items van {1} na {2} + + + Kon nie {0} item van {1} na {2} skuif nie + + + Kon nie {0} items van {1} na {2} skuif nie + + + Kopiëring van {0} item na {1} is gekanselleer + + + Kopiëring van {0} items na {1} is gekanselleer nadat {2} items gekopieer is + + + Kopiëring van {0} items na {1} is gekanselleer nadat {2} item gekopieer is + + + Kopiëring van {0} item na {1} + + + Kopiëring van {0} items na {1} + + + Suksesvol gekopieer {0} item om te {1} + + + Suksesvol gekopieer {0} items om te {1} + + + Skrapping van {0} item uit {1} is gekanselleer + + + Skrapping van {0} items uit {1} is gekanselleer nadat {3} items geskrap is + + + Skrapping van {0} items uit {1} is gekanselleer nadat {3} item geskrap is + + + Skrapping van {0} item uit {1} + + + Skrapping van {0} items uit {1} + + + Suksesvol geskrap {0} item uit {1} + + + Suksesvol geskrap {0} items uit {1} + + + Kon nie {0} item uit {1} skrap nie + + + Kon nie {0} items uit {1} skrap nie + + + die Herwin Bin + + + Skuif items + + + Kopiëring van items + + + Herwinning het misluk + + + Herwinning gekanselleer + + + kanselleer + + + Ontslaan + + + Ontslaan + + + Hulptuie + + + Sinchrostatus + + + Sekuriteit + + + Gevorderde toestemmings + + + Laat toe + + + Weier + + + Volle beheer + + + Lys gidsinhoud + + + Modifiseer + + + Toestemmings vir + + + Lees en voer uit + + + Lees + + + Groep- of gebruikersname + + + Skryf + + + Onbekende rekening + + + Jy het nie toestemmings om die sekuriteitseienskappe van hierdie voorwerp te bekyk nie. Klik op "Gevorderde toestemmings" om voort te gaan. + + + Eienaar + + + Onbekende eienaar + + + Wys die vertikale duimgidsvlieg op die titelbalk + + + Kom meer te wete oor persoonlike temas + + + Kom meer te wete oor persoonlike temas + + + Pasgemaakte temas bied 'n goeie manier vir u om Files te personaliseer. + + + Bekyk dokumentasie. + + + Kies 'n bestemming en onttrek lêers + + + Blaai + + + Maak bestemmingsomslag oop wanneer dit voltooi is + + + Onttrek lêers + + + Onttrek hier + + + Onttrek... + + + Onttrek na {0}\ + + + Skep nuwe biblioteek + + + Herstel verstekbiblioteke + + + Herstel + + + Is jy seker jy wil die verstekbiblioteke herstel? Al jou lêers sal op jou stoor bly. + + + Herstel Biblioteke + + + Eienaar + + + Gevorderde toestemmings vir {0} + + + Spesiale + + + Toegang + + + Van toepassing op + + + Entiteit + + + lêers + + + hierdie omslag + + + subomslae + + + Oorgeneem + + + Nee + + + Toestemmings + + + Soort + + + Jy het nie toestemmings om die sekuriteitseienskappe van hierdie voorwerp te bekyk nie. Jy kan probeer om eienaarskap van hierdie voorwerp te neem. + + + Spesiale + + + Hierdie omslag en lêers + + + Hierdie omslag, subomslae en lêers + + + Slegs lêers + + + Slegs subomslae + + + Slegs subomslae en lêers + + + Slegs hierdie omslag + + + Hierdie omslag en subomslae + + + Voeg data by + + + Verander toestemming + + + Skep omslae + + + Skep lêers + + + Skrap subgidse en lêers + + + Voer lêers uit + + + Lees eienskappe + + + Lees data + + + Lees uitgebreide eienskappe + + + Lees toestemmings + + + Neem eienaarskap + + + Besoek omslag + + + Skryf eienskappe + + + Skryf data + + + Skryf uitgebreide eienskappe + + + Geen + + + Omskep oorgeërfde toestemmings in eksplisiete toestemmings + + + Aktiveer erfenis + + + Verwyder alle oorgeërfde toestemmings + + + Stel kindertoestemmings terug + + + Vervang alle kinderobjektoestemmingsinskrywings met oorerflike toestemmingsinskrywings van hierdie objek + + + Skep Bondel + + + Skep Bondel + + + Maak paneel toe + + + Voer kompakte oorleg in + + + Verlaat kompakte oorleg + + + Lêerbeskrywing + + + Maatskappy + + + Taal + + + Handelsmerke + + + Lêerweergawe + + + Laai volle voorskou + + + Laai die item van die wolk af en laai die voorskou + + + {0} items ({1} lêers, {2} omslae) + + + Ongekompakteerde grootte + + + Wys wolkaandrywers-afdeling + + + Wys aandrywers-afdeling + + + Wys WSL-afdeling + + + Wys netwerkafdeling + + + WSL + + + Versteek {0} afdeling + + + Instellings + + + Kopieer + + + Kopieer + + + Voeg lêer by + + + Voeg omslag by + + + Voeg lêer by... + + + Bywerkings beskikbaar + + + Bywerkings is gereed om te installeer + + + Sluit + + + Werk by + + + Tuis + + + Nuwe duimgids + + + Ctrl+A + + + Alt+Shift++ + + + Ctrl+Shift+W + + + Ctrl+N + + + Ctrl+Alt+Up + + + Ctrl+Alt+Down + + + Ctrl+Shift+1 + + + Ctrl+Shift+2 + + + Ctrl+Shift+3 + + + Ctrl+Shift+4 + + + Ctrl+Shift+5 + + + Ctrl+Shift+6 + + + Ctrl+T + + + Ctrl+Shift+T + + + Die argiefontginning is suksesvol voltooi. + + + Onttrek argief + + + Onttrek voltooi! + + + Ctrl+Shift+N + + + Privaatheidsbeleid + + + Fluent UI-stelselikone + + + Fluent UI-stelselikone + + + *Persoonlike inligting versameling* +Files versamel, stoor, deel of publiseer geen persoonlike inligting nie. + +*Nie-persoonlike inligting versameling* + +Ons gebruik App Center om tred te hou met programgebruik, foute te vind en ongelukke op te los. Alle inligting wat aan App Center gestuur word, is anoniem en vry van enige gebruiker of kontekstuele data. + + + Lêermerker + + + Lêermerker + + + Maak oueromslag oop + + + Onbekende + + + Lêermerker + + + Redigeer lêermerkers + + + Redigeer lêermerkers + + + Redigeer lêermerkers + + + Aktiveer lêermerkers + + + Wys gunstelinge-afdeling + + + Pas op almal toe + + + Deel van stel + + + Maak aandrywer oop + + + Voeg asseblief 'n skyf in aandrywer {0} + + + Voeg 'n skyf in + + + OK + + + Verwysings Benodig + + + Anoniem + + + Verskaf jou verwysings: + + + Wagwoord + + + Gebruikersnaam + + + Bondels Hulptuig + + + Skywe Hulptuig + + + Biblioteke Hulptuig + + + Onlangse Lêers Hulptuig + + + Geen items gevind nie + + + Wys slegs voorskou + + + Maak logligging oop + + + Skep skakel in {0} + + + Wys lêeruitbreidings + + + Wys versteekte items + + + Wys gunstelinge-afdeling + + + Datumformaat + + + Maak vouers in nuwe duimgids oop + + + Pen Herwin Bin na gunstelinge + + + Taal + + + Wys wolkaandrywers-afdeling + + + Wys 'n bevestigingsdialoog wanneer lêers of omslae geskrap word + + + Wys aandrywers-afdeling + + + Wys biblioteekafdeling + + + Wys netwerkafdeling + + + Wys WSL-afdeling + + + Terminaaltoepassing + + + Deurlopende Take + + + Beweeg een af + + + Beweeg een op + + + Beweeg na onder + + + Beweeg na bo + + + Datum geskep kolom + + + Item grootte kolom + + + Versteklêerbestuurder + + + Stel Files as die versteklêerbestuurder op + + + Gebruik Files as oop lêerdialoog + + + Merker + + + Maak omslae oop met 'n enkele klik + + + Omslagpad + + + Voer Instellings Uit + + + Voer Instellings In + + + Bestuur Instellings + + + Kon nie instellings invoer nie. Die instellingslêer is korrup. + + + Fout met die invoer van instellings + + + Maak Herwin Bin Leeg + + + Kantbalk + + + Kies 'n pasgemaakte omslagikoon + + + Aanpassing + + + Herstel verstek + + + Lêers en omslae + + + Maak nuwe geval oop wanneer gidse van die taakbalkspringlys oopgemaak word + + + Opstartinstellings + + + Verenigbaarheid + + + Geen + + + Op Windows-aanmelding + + + Op program begin + + + Toepassing + + + Geen + + + Stelsel + + + Stelsel (gevorderd) + + + Geen + + + Windows 7 + + + Windows 8 + + + Windows Vista + + + Windows Vista SP1 + + + Windows Vista SP2 + + + 16 Bis Kleure (65536) + + + 8 Bis Kleure (256) + + + Geen + + + Verenigbaarheidsmodus + + + Deaktiveer volskermoptimaliserings + + + Hardloop by 640 x 480 skerm resolusie + + + Opsies + + + Oorheers hoë DPI-skaalgedrag + + + Verminderde kleurmodus + + + Registreer vir herbegin + + + Laat loop as administrateur + + + Laat loop verenigbaarheids foutspeurder + + + Gebruik DPI-instellings van die hoofmonitor + + + Derde party lisensies + + + Hierdie instelling verander stelsellêers en kan onverwagte newe-effekte op jou toestel hê. Die ontwikkelaars neem geen verantwoordelikheid in die geval dat 'n probleem as gevolg daarvan plaasvind nie. Om voort te gaan met hierdie opsie is 'n erkenning van die risiko's wat by hierdie aksie betrokke is. Let asseblief daarop dat die deïnstalleer van Files nie hierdie veranderinge sal ongedaan maak nie en kan verhoed dat jy File Explorer oopmaak, tensy jy hierdie instelling afskakel voordat jy Files van jou toestel verwyder. + \ No newline at end of file diff --git a/src/Files/Strings/de-DE/Resources.resw b/src/Files/Strings/de-DE/Resources.resw index 0af73574db98..e7b54c910ded 100644 --- a/src/Files/Strings/de-DE/Resources.resw +++ b/src/Files/Strings/de-DE/Resources.resw @@ -115,7 +115,7 @@ Einfügen - Mit Terminal öffnen... + In Terminal öffnen... Ordner @@ -187,7 +187,7 @@ Größe - Willkommen zu Files! + Willkommen bei Files! Erlaubnis erteilen @@ -610,7 +610,7 @@ Mitwirkende - Release Notes + Versionshinweise Das Element, auf das verwiesen wurde, ist fehlerhaft oder nicht erreichbar.{0}Fehlermeldung:{0}{1} @@ -1093,7 +1093,7 @@ Status Center - Ergebnisse suchen in + Suchergebnisse in {1} für {0} Suchergebnisse @@ -1204,7 +1204,7 @@ Dateipfad - Vorschau anzeigen/ausblenden (Strg+P) + Vorschauleiste anzeigen/ausblenden (Strg+P) Bestätigen @@ -1273,7 +1273,7 @@ Bestätigen - Sammlungname eingeben + Sammlungsnamen eingeben Sammlung erstellen @@ -1954,7 +1954,7 @@ Unbekannter Account - Sie haben keine Berechtigung die Sicherheitseigenschaften dieses Objektes anzusehen. Klicken Sie auf "Erweiterte Rechte", um fortzufahren. + Sie sind nicht berechtigt, die Sicherheitseigenschaften dieses Objektes anzusehen. Klicken Sie auf "Erweiterte Rechte", um fortzufahren. Besitzer @@ -1975,7 +1975,7 @@ Mehr über benutzerdefinierte Themes erfahren - Benutzerdefinierte Themes sind ein hervoragender Weg Files zu personalisieren. + Benutzerdefinierte Themes sind ein hervorragendes Mittel, um Files zu personalisieren. Dokumentation anzeigen. @@ -1999,7 +1999,7 @@ Wiederherstellen - Sind Sie sicher, dass die die Standardbibliotheken wiederherstellen möchten? Alle Dateien verbleiben auf der Festplatte. + Sind Sie sicher, dass Sie die Standardbibliotheken wiederherstellen möchten? Alle Dateien verbleiben auf der Festplatte. Bibliotheken wiederherstellen @@ -2050,7 +2050,7 @@ Typ - Du hast keine Berechtigung die Sicherheitseingenschaften dieses Objektes anzuschauen. Du kannst versuchen Besitzer dieses Objektes zu werden + Du bist nicht berechtigt, die Sicherheitseingenschaften dieses Objektes anzuschauen. Du kannst versuchen Besitzer dieses Objektes zu werden Speziell @@ -2230,7 +2230,7 @@ Netzwerklaufwerke anzeigen - Papierkorp zu Favoriten hinzufügen + Papierkorb zu Favoriten hinzufügen Bibliotheken anzeigen @@ -2254,16 +2254,16 @@ Versteckte Dateien und Ordner anzeigen - Geschützte Systemdateien ausblenden (Empfohlen) + Geschützte Systemdateien ausblenden (empfohlen) - Öffnen durch einfachen Klick + Dateien durch durch einfachen Klick öffnen Dateien und Ordner zusammen auflisten und sortieren - Nicht indizierte Dateien und Ordner in der Suche anzeigen (Suchen wird langsamer) + Nicht indizierte Dateien und Ordner in der Suche anzeigen Individuelle Einstellungen für einzelne Ordner aktivieren @@ -2399,12 +2399,12 @@ -*Sammeln von personenbezogenden Daten* +*Sammeln von personenbezogenden Daten* Files sammelt, speichert, teilt oder veröffentlicht keine persönlichen Informationen. *Sammeln von nicht personenbezogenen Daten* -Wir benutzen das App Center um einen Überblick über die Nutzung von Files zu erhalten, um Fehler zu finden und diese zu beheben. Alle Informationen die an das App Center gesendet werden sind anonym und enthalten keinerlei personen- oder kontextbezogenden Informationen. +Wir benutzen das App Center, um einen Überblick über die Nutzung von Files zu erhalten, um Fehler zu finden und diese zu beheben. Alle Informationen, die an das App Center gesendet werden, sind anonym und enthalten keinerlei personen- oder kontextbezogende Informationen. @@ -2524,24 +2524,6 @@ Wir benutzen das App Center um einen Überblick über die Nutzung von Files zu e Link in {0} erstellen - - Spalten - - - Details - - - Große Symbole - - - Mittelgroße Symbole - - - Kleine Symbole - - - Kacheln - Dateierweiterungen anzeigen @@ -2558,7 +2540,7 @@ Wir benutzen das App Center um einen Überblick über die Nutzung von Files zu e Favoritenbereich anzeigen - Papierkorp zu Favoriten hinzufügen + Papierkorb zu Favoriten hinzufügen Cloud-Laufwerksbereich anzeigen @@ -2624,7 +2606,7 @@ Wir benutzen das App Center um einen Überblick über die Nutzung von Files zu e Tag - Dateien mit einfachem Klick öffnen + Ordner mit einfachem Klick öffnen Ordnerpfad @@ -2642,7 +2624,7 @@ Wir benutzen das App Center um einen Überblick über die Nutzung von Files zu e Einstellungen konnten nicht importiert werden, da die Einstellungsdatei fehlerhaft ist. - Fehler beim importieren der Einstellungen + Fehler beim Importieren der Einstellungen Neues Ordnersymbol auswählen @@ -2675,7 +2657,7 @@ Wir benutzen das App Center um einen Überblick über die Nutzung von Files zu e System - System (Erweitert) + System (erweitert) Keine @@ -2734,4 +2716,34 @@ Wir benutzen das App Center um einen Überblick über die Nutzung von Files zu e DPI-Einstellungen auf Hauptmonitor anwenden + + Standard-Dateimanager + + + Files als Standard-Dateimanager einstellen + + + Papierkorb leeren + + + Seitenleiste + + + Dateien und Ordner + + + Neue Instanz öffnen, wenn Ordner von der Taskleisten-Sprungliste geöffnet werden + + + Starteinstellungen + + + Lizenzen von Drittanbietern + + + Diese Einstellung modifiziert Systemdateien und kann unerwartete Seiteneffekte mit sich bringen. Die Entwickler übernehmen keine Verantwortung, falls ein Problem auftritt. Mit dieser Einstellung fortzufahren ist eine Bestätigung dafür, dass Ihnen dieses Risiko bekannt ist. Bitte beachten Sie, dass das Deinstallieren von Files diese Änderungen nicht rückgängig macht. Es kann vorkommen, dass Sie den Explorer nicht mehr öffnen können, außer Sie deaktivieren diese Einstellung vor der Deinstallation von Files. + + + Alles löschen + \ No newline at end of file diff --git a/src/Files/Strings/el/Resources.resw b/src/Files/Strings/el/Resources.resw index 42d4302a6bc8..5dfe0c7b604b 100644 --- a/src/Files/Strings/el/Resources.resw +++ b/src/Files/Strings/el/Resources.resw @@ -31,7 +31,7 @@ Έγγραφο κειμένου - Τοποθεσία αντιγραφής + Aντιγραφή τοποθεσίας Επικόλληση @@ -64,7 +64,7 @@ Όνομα στοιχείου - Διαδρομή + Διαδρομή: Μέγεθος: @@ -100,7 +100,7 @@ Τροποποιήθηκε: - Πρόσβαση: + Προσπελάστηκε: Εκκαθάριση όλων των στοιχείων @@ -112,10 +112,10 @@ Αναζήτηση - Τα αρχεία στα οποία είχατε πρόσβαση στο παρελθόν θα εμφανιστούν εδώ + Τα αρχεία τα οποία προσπελάσατε στο παρελθόν θα εμφανιστούν εδώ - Τοποθεσία ανοίγματος αρχείου + Ανοίγμα θέσης αρχείου Αφαιρέστε αυτό το στοιχείο @@ -211,10 +211,10 @@ Μέγεθος - Ανερχόμενο + Αύξουσα σειρά - Κατιούσα + Φθίνουσα σειρά Ανανέωση @@ -229,7 +229,7 @@ MD5Hash: - Νέο + Δημιουργία Φάκελος @@ -382,10 +382,10 @@ Τοποθετήστε την απαραίτητη μονάδα δίσκου για να αποκτήσετε πρόσβαση σε αυτό το στοιχείο. - Μονάδα δίσκου αποσυνδεδεμένη + Η μονάδα δίσκου αποσυνδέθηκε - Το αρχείο το οποίο προσπαθείτε να αποκτήσετε πρόσβαση ενδέχεται να έχει μετακινηθεί ή διαγραφεί. + Το αρχείο που προσπαθείτε να προσπελάσετε ενδέχεται να έχει μετακινηθεί ή διαγραφεί. Το αρχείο δεν βρέθηκε @@ -394,10 +394,10 @@ Το αρχείο που προσπαθείτε να κάνετε προεπισκόπηση ενδέχεται να έχει μετακινηθεί ή διαγραφεί. - Ο φάκελος στον οποίο προσπαθείτε να αποκτήσετε πρόσβαση μπορεί να έχει μετακινηθεί ή διαγραφεί. + Ο φάκελος που προσπαθείτε να προσπελάσετε ενδέχεται να έχει μετακινηθεί ή διαγραφεί. - Διαγράψατε αυτόν τον φάκελο? + Διαγράψατε αυτόν τον φάκελο; Το αρχείο που προσπαθείτε να διαγράψετε χρησιμοποιείται επί του παρόντος από μια άλλη εφαρμογή. @@ -457,7 +457,7 @@ Η ζητούμενη λειτουργία δεν υποστηρίζεται - {0} χωρίς {1} + {0} ελεύθερα από {1} Άνοιγμα με @@ -496,13 +496,13 @@ Ναι - Είστε σίγουροι ότι θέλετε να διαγράψετε οριστικά όλα αυτά τα στοιχεία? + Είστε σίγουροι ότι θέλετε να διαγράψετε οριστικά όλα αυτά τα στοιχεία; Άδειασμα του κάδου ανακύκλωσης - ψηφιολέξεις + byte KB @@ -523,7 +523,7 @@ B - Εκτελέστε ως διαχειριστής + Εκτέλεση ως διαχειριστής Ξεκλείδωμα @@ -562,7 +562,7 @@ Όλα σε {0} - Χρησιμοποιημένος χώρος: + Δεσμευμένος χώρος: Ελεύθερος χώρος: @@ -616,7 +616,7 @@ Άνοιγμα θέσης αρχείου - Επιχειρήματα: + Παράμετροι: Προορισμός: @@ -655,13 +655,13 @@ {0} - συντόμευση - Τοποθεσία ανοίγματος αρχείου + Άνοιγμα θέσης αρχείου Άγνωστο - Ανοιχτή θέση καταγραφής + Άνοιγμα τοποθεσίας καταγραφής Το Files αντιμετώπισε ένα πρόβλημα για το οποίο οι προγραμματιστές δεν είχαν προετοιμαστεί ακόμα. @@ -688,10 +688,10 @@ Έκδοση: - Δεν υπάρχει τίποτα να μοιραστώ αυτή τη στιγμή... + Δεν υπάρχει τίποτα για κοινή χρήση αυτή τη στιγμή... - Τα στοιχεία που έχετε επιλέξει θα μοιραστούν + Τα στοιχεία που έχετε επιλέξει θα διαμοιραστούν Το επιλεγμένο στοιχείο θα διαμοιραστεί @@ -730,7 +730,7 @@ Ναι - Η εφαρμογή πρέπει να επανεκκινηθεί για να εφαρμοστούν αυτές οι ρυθμίσεις, θα θέλατε να επανεκκινήσετε την εφαρμογή? + Η εφαρμογή πρέπει να επανεκκινηθεί για να εφαρμοστούν αυτές οι ρυθμίσεις, θα θέλατε να επανεκκινήσετε την εφαρμογή; Υποστηρίξτε μας @@ -853,7 +853,7 @@ Ημερομηνία τροποποίησης - Βάθος ψηφίων + Βάθος bit Διαστάσεις @@ -994,7 +994,7 @@ Παραγωγός - Url προώθησης + Url προώθησης Στυλ παρόχου @@ -1078,10 +1078,10 @@ Συμπίεση - Πλάτος πλαισίου + Πλάτος καρέ - Ύψος πλαισίου + Ύψος καρέ Προσανατολισμός @@ -1099,7 +1099,7 @@ GPS - Μέσα ενημέρωσης + Μέσα Ήχος @@ -1123,7 +1123,7 @@ Επιλέξτε - Άνοιγμα αντικειμένων με ένα κλικ + Άνοιγμα αρχείων με ένα κλικ Αρχείο @@ -1138,13 +1138,13 @@ Στοιχείο συντόμευσης - Οδηγίες + Μονάδες Δίσκου Μετακίνηση εδώ - Ασφαλής αφαίρεση υλικού + Ασφαλής κατάργηση υλικού Η συσκευή μπορεί τώρα να αφαιρεθεί με ασφάλεια από τον υπολογιστή. @@ -1156,7 +1156,7 @@ Αυτή η συσκευή είναι σε χρήση. Κλείστε τυχόν προγράμματα, παράθυρα ή καρτέλες που ενδέχεται να χρησιμοποιούν τη συσκευή και, στη συνέχεια, δοκιμάστε ξανά. - Έξοδος + Εξαγωγή Δημιουργία διπλότυπου Καρτέλας @@ -1180,7 +1180,7 @@ Ορισμένες ιδιότητες ενδέχεται να περιέχουν προσωπικές πληροφορίες. - Εκκαθάριση + Εκκαθάριση Εκκαθάριση όλων των ιδιοτήτων @@ -1201,7 +1201,7 @@ Αποτελέσματα αναζήτησης - Δείτε ποιος συνέβαλε στο Files + Δείτε ποιοι συνέβαλαν στο Files Μάθετε τι νέο υπάρχει στο Files @@ -1213,7 +1213,7 @@ Υποστηρίξτε μας στο PayPal - Λίστα και ταξινόμηση καταλόγων μαζί με αρχεία + Απαρίθμηση και ταξινόμηση καταλόγων μαζί με αρχεία Λεπτομέρειες @@ -1231,7 +1231,7 @@ Απόκρυψη προστατευμένων αρχείων λειτουργικού συστήματος (Συνιστάται) - Εμφάνιση μη ευρετηριασμένων στοιχείων κατά την αναζήτηση αρχείων και φακέλων (οι αναζητήσεις μπορεί να διαρκέσουν περισσότερο) + Εμφάνιση μη ευρετηριασμένων στοιχείων κατά την αναζήτηση αρχείων και φακέλων Ενεργοποίηση ατομικών προτιμήσεων για μεμονωμένους καταλόγους @@ -1336,7 +1336,7 @@ Εξαγωγή δεσμών - Δέσμες εισαγωγής + Εισαγωγή δεσμών Η δέσμη με το ίδιο όνομα υπάρχει ήδη! @@ -1357,10 +1357,10 @@ Άνοιγμα σε νέα καρτέλα - Τοποθεσία ανοικτού στοιχείου + Άνοιγμα τοποθεσίας στοιχείου - Αφαιρέστε από τη δέσμη + Αφαίρεση από τη δέσμη Αφαίρεση πακέτου @@ -1369,7 +1369,7 @@ Δέσμες - Σύρετε και αφήστε οποιοδήποτε αρχείο ή φάκελο εδώ για γρήγορη πρόσβαση σε αυτό + Σύρετε οποιοδήποτε αρχείο ή φάκελο εδώ για γρήγορη πρόσβαση σε αυτό Επιβεβαίωση @@ -1471,7 +1471,7 @@ Πολυεπιλογή - Εάν αλλάξετε μια επέκταση αρχείου, το αρχείο ενδέχεται να καταστεί άχρηστο. Είστε σίγουρος ότι θέλετε να την αλλάξετε? + Εάν αλλάξετε μια επέκταση αρχείου, το αρχείο ενδέχεται να καταστεί άχρηστο. Είστε σίγουρος ότι θέλετε να την αλλάξετε; Μονάδα CD ROM @@ -1519,7 +1519,7 @@ Αποσύνδεση - Περισσότερες επιλογές πακέτων + Περισσότερες επιλογές δεσμών Περισσότερες επιλογές δίσκων @@ -1555,10 +1555,10 @@ Αριθμός αντικειμένων - Κλείστε τις καρτέλες προς τα δεξιά + Κλείσιμο καρτελών δεξιά - Διαγραφή Απέτυχε + Η διαγραφή απέτυχε Εμφανίστηκε ένα άγνωστο σφάλμα. @@ -1573,13 +1573,13 @@ Η λειτουργία ολοκληρώθηκε. - Ανακύκλωση πλήρης + Η ανακύκλωση ολοκληρώθηκε - Μετακίνηση ολοκληρώθηκε + Η μετακίνηση ολοκληρώθηκε - Αντιγραφή πλήρης + Η αντιγραφή ολοκληρώθηκε Όχι @@ -1588,7 +1588,7 @@ Αυτή η ενέργεια απαιτεί δικαιώματα διαχειριστή - Θα θέλατε να συνεχίσετε ως διαχειριστής? + Θα θέλατε να συνεχίσετε ως διαχειριστής; Το Files εκτελείται ως διαχειριστής @@ -1606,7 +1606,7 @@ Καρφίτσωμα στο μενού Έναρξη - Αποσύνδεση από το μενού Έναρξη + Ξεκαρφίτσωμα από το μενού Έναρξη Στήλες @@ -1657,10 +1657,10 @@ Δημιουργία - Ανοιχτή αίσθηση αποθήκευσης + Άνοιγμα ελέγχου αποθήκευσης - Ανοιχτή αίσθηση αποθήκευσης + Άνοιγμα ελέγχου αποθήκευσης Αντιγραφή @@ -1714,16 +1714,16 @@ Ταχύτητα ISO - Εμφάνιση widget πακέτων + Εμφάνιση widget δεσμών - Εμφάνιση widget Οδηγίες + Εμφάνιση widget δίσκων Εμφάνιση widget φακέλων - Εμφάνιση widget Πρόσφατα αρχεία + Εμφάνιση widget πρόσφατων αρχείων Διαχείριση Widgets @@ -1732,7 +1732,7 @@ Διαχείριση widgets - Δεν βρήκατε αυτό που ψάχνατε? + Δεν βρήκατε αυτό που ψάχνατε; Αναζήτηση μη ευρετηριασμένων στοιχείων. @@ -1774,7 +1774,7 @@ Υπάρχει ένα αντικρουόμενο όνομα αρχείου. - Δημιουργήστε ένα φάκελο με επιλογή + Δημιουργία φακέλου με επιλογή Όνομα: @@ -1786,22 +1786,22 @@ Ημερομηνία τροποποίησης: - Ανοίξτε με + Άνοιγμα με Εικονίδιο αρχείου - Αρχική στήλη διαδρομής + Στήλη αρχικής διαδρομής Στήλη τύπου στοιχείου - Στήλη Ημερομηνία τροποποίησης + Στήλη ημερομηνίας τροποποίησης - Στήλη Ημερομηνία διαγραφής + Στήλη ημερομηνίας διαγραφής Επαναπροσδιορισμός πλευρικής γραμμής @@ -1816,7 +1816,7 @@ Ταξινόμηση - Ομάδα από + Ομαδοποίηση κατά Ταξινόμηση κατά @@ -1876,7 +1876,7 @@ {0} στοιχεία - Άνοιγμα κλειστής καρτέλας + Άνοιγμα καρτέλας που έχει κλείσει Πολιτική απορρήτου @@ -1909,16 +1909,16 @@ Μετακίνηση {0} στοιχείων από {1} σε {2} - Μετακινήσατε επιτυχώς {0} αντικείμενο από {1} σε {2} + Επιτυχής μετακίνηση {0} αντικειμένου από {1} σε {2} - Μετακινήσατε επιτυχώς {0} αντικείμενα από {1} σε {2} + Επιτυχής μετακίνηση {0} αντικειμένων από {1} σε {2} Απέτυχε η μετακίνηση του στοιχείου {0} από το {1} στο {2} - Απέτυχε η μετακίνηση {0} αντικειμένων από {1} σε {2} + Η μετακίνηση {0} αντικειμένων από {1} σε {2} απέτυχε Η αντιγραφή του στοιχείου {0} στο {1} ακυρώθηκε @@ -1981,7 +1981,7 @@ Η ανακύκλωση απέτυχε - Ανακύκλωση ακυρώθηκε + Η ανακύκλωση ακυρώθηκε ακύρωση @@ -2002,7 +2002,7 @@ Προηγμένα δικαιώματα - Επιτρέψτε + Αποδοχή Άρνηση @@ -2020,10 +2020,10 @@ Δικαιώματα για - Διαβάστε και εκτελέστε + Ανάγνωση και εκτέλεση - Διαβάστε + Ανάγνωση Ονόματα ομάδων ή χρηστών @@ -2071,13 +2071,13 @@ Εξαγωγή αρχείων - Απόσπασμα εδώ + Εξαγωγή εδώ - Απόσπασμα... + Εξαγωγή... - Απόσπασμα στο {0}\ + Εξαγωγή στο {0}\ Δημιουργία νέας βιβλιοθήκης @@ -2089,7 +2089,7 @@ Επαναφορά - Είστε σίγουροι ότι θέλετε να επαναφέρετε τις προεπιλεγμένες βιβλιοθήκες? Όλα τα αρχεία σας θα παραμείνουν στον αποθηκευτικό σας χώρο. + Είστε σίγουροι ότι θέλετε να επαναφέρετε τις προεπιλεγμένες βιβλιοθήκες; Όλα τα αρχεία σας θα παραμείνουν στον αποθηκευτικό σας χώρο. Επαναφορά βιβλιοθηκών @@ -2161,7 +2161,7 @@ Αυτός ο φάκελος και οι υποφάκελοι - Προσθήκη δεδομένων + Προσάρτηση δεδομένων Αλλαγή άδειας @@ -2182,31 +2182,31 @@ Εκτέλεση αρχείων - Διαβάστε χαρακτηριστικά + Χαρακτηριστικά ανάγνωσης - Διαβάστε δεδομένα + Ανάγνωση δεδομένων - Ανάγνωση εκτεταμένων χαρακτηριστικών + Πρόσθετα χαρακτηριστικά ανάγνωσης - Διαβάστε τις άδειες + Δικαιώματα ανάγνωσης - Αναλάβετε την ιδιοκτησία + Ανάληψη κατοχής - Επισκεφθείτε το φάκελο + Προσπέλαση φακέλου - Γράψτε χαρακτηριστικά + Χαρακτηριστικά εγγραφής - Γράψτε δεδομένα + Εγγραφή δεδομένων - Γράψτε εκτεταμένα χαρακτηριστικά + Πρόσθετα χαρακτηριστικά εγγραφής Κανένα @@ -2239,7 +2239,7 @@ Εισαγωγή συμπαγούς επικάλυψης - Έξοδος συμπαγής επικάλυψη + Έξοδος από συμπαγή επικάλυψη Περιγραφή αρχείου @@ -2362,7 +2362,7 @@ Ctrl+Shift+T - Τερματικές εφαρμογές + Εφαρμογές τερματικού Η εξαγωγή του αρχείου ολοκληρώθηκε με επιτυχία. @@ -2377,7 +2377,7 @@ Ctrl+Shift+N - Επεξεργασία τερματικών εφαρμογών + Επεξεργασία εφαρμογών τερματικού Μορφή ημερομηνίας @@ -2433,10 +2433,10 @@ Τύπος - Ανερχόμενο + Αύξουσα σειρά - Κατιούσα + Φθίνουσα σειρά Κατάσταση συγχρονισμού @@ -2511,38 +2511,20 @@ Widget δέσμες - Widget Δίσκων + Widget δίσκων - Widget Βιβλιοθήκες + Widget βιβλιοθηκών - Widget Πρόσφατα αρχεία + Widget πρόσφατων αρχείων - Ανοιχτή θέση καταγραφής + Άνοιγμα τοποθεσίας καταγραφών Δημιουργία συνδέσμου στο {0} - - Στήλες - - - Λεπτομέρειες - - - Μεγάλα εικονίδια - - - Μεσαία εικονίδια - - - Μικρά εικονίδια - - - Πλακίδια - Εμφάνιση επεκτάσεων αρχείων @@ -2589,7 +2571,7 @@ Εμφάνιση διαλόγου επιβεβαίωσης κατά τη διαγραφή αρχείων ή φακέλων - Τερματικές εφαρμογές + Εφαρμογές τερματικού Συνεχιζόμενα καθήκοντα @@ -2601,25 +2583,25 @@ Προβολή προεπισκόπησης μόνο - Μετακινήστε ένα προς τα κάτω + Μετακίνηση προς τα κάτω - Μετακινήστε ένα προς τα πάνω + Μετακίνηση προς τα πάνω - Μετακίνηση στο κάτω μέρος + Μετακίνηση στο τέλος Μετακίνηση στην κορυφή - Στήλη Ημερομηνία δημιουργίας + Στήλη ημερομηνίας δημιουργίας - Στήλη Μέγεθος στοιχείου + Στήλη μεγέθους στοιχείου - Χρησιμοποιήση Files ως παράθυρο διαλόγου Άνοιγμα αρχείων + Χρήση Files ως παράθυρο διαλόγου για άνοιγμα αρχείων Ετικέτα @@ -2735,4 +2717,31 @@ Χρησιμοποίηση ρυθμίσεων DPI της κύριας οθόνης + + Προεπιλεγμένο πρόγραμμα διαχείρησης αρχείων + + + Ορισμός του Files ως το προεπιλεγμένο πρόγραμμα διαχείρησης αρχείων + + + Άδειασμα κάδου ανακύκλωσης + + + Πλαϊνή μπάρα + + + Αρχεία και φάκελοι + + + Ρυθμίσεις εκκίνησης + + + Άδειες τρίτων + + + Αυτή η ρύθμιση τροποποιεί αρχεία του συστήματος και μπορεί να έχει απροσδόκητες παρενέργειες στη συσκευή σας. Οι προγραμματιστές δεν λαμβάνουν καμία ευθύνη σε περίπτωση που κάποιο πρόβλημα προκύψει ως αποτέλεσμα. Αν συνεχίσετε με αυτή την επιλογή αναγνωρίζετε τους κινδύνους που ενέχει αυτή η ενέργεια. Προσέξτε πως η απεγκατάση του Files δεν θα αναιρέσει αυτές τις αλλαγές και μπορεί να σας αποτρέψει από το άνοιγμα της εξερεύνησης αρχείων, εκτός αν απενεργοποιήσετε αυτή τη ρύθμιση πριν αφαιρέσετε το Files από τη συσκευή σας. + + + Εκκαθάριση όλων + \ No newline at end of file diff --git a/src/Files/Strings/en-GB/Resources.resw b/src/Files/Strings/en-GB/Resources.resw index 05b874f143d5..c1d91a96e327 100644 --- a/src/Files/Strings/en-GB/Resources.resw +++ b/src/Files/Strings/en-GB/Resources.resw @@ -1243,7 +1243,7 @@ Hide protected operating system files (Recommended) - Show unindexed items when searching for files and folders (searches may take longer) + Show unindexed items when searching for files and folders Enable individual preferences for individual directories @@ -2524,24 +2524,6 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Create link in {0} - - Columns - - - Details - - - Large Icons - - - Medium Icons - - - Small Icons - - - Tiles - Show file extensions @@ -2734,4 +2716,34 @@ We use App Center to keep track of app usage, find bugs, and fix crashes. All in Use DPI settings of the main monitor + + Default file manager + + + Set Files as the default file manager + + + Empty Recycle Bin + + + Sidebar + + + Files and folders + + + Open new instance when opening directories from the taskbar jumplist + + + Startup settings + + + Third party licences + + + This setting modifies system files and can have unexpected side effects on your device. The developers take no responsibility in the event an issue occurs as a result. Continuing with this option is an acknowledgment of the risks involved with this action. Please note, uninstalling Files will not undo these changes and may prevent you from opening file explorer unless you turn off this setting before removing Files from your device. + + + Clear all + \ No newline at end of file diff --git a/src/Files/Strings/es-419/Resources.resw b/src/Files/Strings/es-419/Resources.resw index d8c95b6b1f67..9b32683aeab3 100644 --- a/src/Files/Strings/es-419/Resources.resw +++ b/src/Files/Strings/es-419/Resources.resw @@ -2512,24 +2512,6 @@ Usamos App Center para realizar un seguimiento del uso de la aplicación, encont Crear vínculo en {0} - - Columnas - - - Detalles - - - Iconos grandes - - - Iconos medianos - - - Iconos pequeños - - - Mosaicos - Mostrar extensiones de archivo diff --git a/src/Files/Strings/es-ES/Resources.resw b/src/Files/Strings/es-ES/Resources.resw index ef2d04604a25..a32d0a9e78f5 100644 --- a/src/Files/Strings/es-ES/Resources.resw +++ b/src/Files/Strings/es-ES/Resources.resw @@ -2523,24 +2523,6 @@ Utilizamos App Center para hacer un seguimiento del uso de la aplicación, encon Crear vínculo en {0} - - Columnas - - - Detalles - - - Iconos grandes - - - Iconos medianos - - - Iconos pequeños - - - Iconos - Mostrar extensiones de archivo diff --git a/src/Files/Strings/fr-FR/Resources.resw b/src/Files/Strings/fr-FR/Resources.resw index 84f66994adee..ccfb1736b721 100644 --- a/src/Files/Strings/fr-FR/Resources.resw +++ b/src/Files/Strings/fr-FR/Resources.resw @@ -2524,24 +2524,6 @@ Nous utilisons l'App Center pour suivre l'utilisation de l'application, trouver Créer un lien dans {0} - - Colonnes - - - Détails - - - Grandes Icônes - - - Moyennes Icônes - - - Petites Icônes - - - Tuiles - Afficher l'extension des fichiers diff --git a/src/Files/Strings/hr-HR/Resources.resw b/src/Files/Strings/hr-HR/Resources.resw index 71623bc871a3..fc3e22beb9e9 100644 --- a/src/Files/Strings/hr-HR/Resources.resw +++ b/src/Files/Strings/hr-HR/Resources.resw @@ -2523,24 +2523,6 @@ Koristimo App Center za praćenje korištenja aplikacija, pronalaženje grešaka Napravi vezu u {0} - - Stupci - - - Detalji - - - Velike ikone - - - Srednje ikone - - - Male ikone - - - Pločice - Prikaži ekstenzije datoteka diff --git a/src/Files/Strings/hu-HU/Resources.resw b/src/Files/Strings/hu-HU/Resources.resw index d536ad6b16d7..9dc30f88c1a4 100644 --- a/src/Files/Strings/hu-HU/Resources.resw +++ b/src/Files/Strings/hu-HU/Resources.resw @@ -2525,24 +2525,6 @@ Az App Center szolgáltatásait használjuk az app használatának monitorozás Parancsikon létrehozása itt: {0} - - Oszlopok - - - Részletek - - - Nagy ikonok - - - Közepes ikonok - - - Kis ikonok - - - Lista - Fájlkiterjesztések megjelenítése diff --git a/src/Files/Strings/id-ID/Resources.resw b/src/Files/Strings/id-ID/Resources.resw index da398d28df46..28a5b46e019d 100644 --- a/src/Files/Strings/id-ID/Resources.resw +++ b/src/Files/Strings/id-ID/Resources.resw @@ -2497,24 +2497,6 @@ Kami menggunakan App Center untuk melacak penggunaan aplikasi, menemukan bug, da Buat tautan di {0} - - Kolom - - - Rincian - - - Ikon Besar - - - Ikon Sedang - - - Ikon Kecil - - - Ubin - Tampilkan ekstensi file diff --git a/src/Files/Strings/it-IT/Resources.resw b/src/Files/Strings/it-IT/Resources.resw index c7656cbc3312..60e0c8e67e37 100644 --- a/src/Files/Strings/it-IT/Resources.resw +++ b/src/Files/Strings/it-IT/Resources.resw @@ -2524,24 +2524,6 @@ Usiamo App Center per tenere traccia dell'utilizzo dell'app, trovare bug e risol Crea collegamento in {0} - - Colonne - - - Dettagli - - - Icone Grandi - - - Icone Medie - - - Icone Piccole - - - Riquadri - Mostra estensioni file diff --git a/src/Files/Strings/ja-JP/Resources.resw b/src/Files/Strings/ja-JP/Resources.resw index e0c1a1e15c37..9c752139c157 100644 --- a/src/Files/Strings/ja-JP/Resources.resw +++ b/src/Files/Strings/ja-JP/Resources.resw @@ -2523,24 +2523,6 @@ App Centerを使用して、アプリの使用状況を追跡し、バグを見 {0} にリンクを作成 - - - - - 詳細 - - - 大きいアイコン - - - 中アイコン - - - 小さいアイコン - - - タイル - ファイル拡張子を表示 diff --git a/src/Files/Strings/ka/Resources.resw b/src/Files/Strings/ka/Resources.resw index 3176953c25fe..6f0139333cf4 100644 --- a/src/Files/Strings/ka/Resources.resw +++ b/src/Files/Strings/ka/Resources.resw @@ -2523,24 +2523,6 @@ ბმულის შექმნა {0} -ში - - სვეტები - - - დეტალები - - - მსხვილი ხატულები - - - საშუალო ხატულები - - - პატარა ხატულები - - - ფილები - ფაილის გაფართოებების ჩვენება diff --git a/src/Files/Strings/ko-KR/Resources.resw b/src/Files/Strings/ko-KR/Resources.resw index 405a9e211768..d03dfd53a1b2 100644 --- a/src/Files/Strings/ko-KR/Resources.resw +++ b/src/Files/Strings/ko-KR/Resources.resw @@ -256,7 +256,7 @@ 대상 폴더 - 는 원본 폴더의 하위 폴더입니다 + 은(는) 원본 폴더의 하위 폴더입니다 건너뛰기 @@ -418,19 +418,19 @@ 바이트 - KB + 킬로바이트(KB) - MB + 메가바이트(MB) - GB + 기가바이트(GB) - TB + 테라바이트(TB) - PB + 페타바이트(PB) B @@ -2392,10 +2392,10 @@ 개인정보취급방침 - Fluent UI System Icons + 플루언트(Fluent) UI 시스템 아이콘 - Fluent UI System Icons + 플루언트(Fluent) UI 시스템 아이콘 @@ -2524,24 +2524,6 @@ Files는 어떤 개인정보도 수집, 저장, 공유, 게시하지 않습니 {0} 폴더로 링크 생성 - - 열 보기 - - - 자세히 - - - 큰 아이콘 - - - 중간 아이콘 - - - 작은 아이콘 - - - 타일 - 파일 확장자 표시 @@ -2681,19 +2663,19 @@ Files는 어떤 개인정보도 수집, 저장, 공유, 게시하지 않습니 없음 - Windows 7 + 윈도우 7 - Windows 8 + 윈도우 8 - Windows Vista + 윈도우 비스타(Vista) - Windows Vista SP1 + 윈도우 비스타(Vista) SP1 - Windows Vista SP2 + 윈도우 비스타(Vista) SP2 16비트 색상 (65536) @@ -2734,4 +2716,34 @@ Files는 어떤 개인정보도 수집, 저장, 공유, 게시하지 않습니 주 모니터의 DPI 설정 사용 + + 빈 휴지통 + + + 사이드바 + + + 파일들과 폴더들 + + + 작업 표시줄 점프 목록에서 디렉터리를 열 때 새 인스턴스에서 열기 + + + 프로그램 시작시 설정 + + + 기본 파일 탐색기 + + + Files를 기본 파일 탐색기로 설정 + + + 제 3자 라이선스 + + + 이 설정은 시스템 파일을 수정하며 예기치 않은 부작용을 낳을 수 있습니다. 개발자는 이로 인해서 일어날 일에 대해 책임을 지지 않습니다. 이 행동으로 일어날 위험에 대해서 인지하고 이 옵션을 계속합니다. Files를 제거하는 것으로는 이 설정을 되돌릴 수 없으며, 기기에서 Files를 제거하기 전 이 설정을 끄기 전까지는 파일 탐색기를 열지 못할 수도 있습니다. + + + 모두 비우기 + \ No newline at end of file diff --git a/src/Files/Strings/lv-LV/Resources.resw b/src/Files/Strings/lv-LV/Resources.resw index 41936b5dc57e..b1739582acb6 100644 --- a/src/Files/Strings/lv-LV/Resources.resw +++ b/src/Files/Strings/lv-LV/Resources.resw @@ -2476,24 +2476,6 @@ Mēs lietojam App Center, lai sekotu līdzi programmas lietojumam, atrastu un no Izveidot saiti {0} - - Kolonnas - - - Detaļas - - - Lielas ikonas - - - Vidēji lielas ikonas - - - Mazas ikonas - - - Mozaīka - Rādīt failu paplašinājumus diff --git a/src/Files/Strings/pl-PL/Resources.resw b/src/Files/Strings/pl-PL/Resources.resw index d7196eac22a2..85990748d43c 100644 --- a/src/Files/Strings/pl-PL/Resources.resw +++ b/src/Files/Strings/pl-PL/Resources.resw @@ -2524,24 +2524,6 @@ Korzystamy z App Center, aby śledzić wykorzystanie aplikacji, znajdować błę Utwórz link w {0} - - Kolumny - - - Właściwości - - - Duże ikony - - - Średnie ikony - - - Małe ikony - - - Kafelki - Pokaż rozszerzenia plików diff --git a/src/Files/Strings/pt-BR/Resources.resw b/src/Files/Strings/pt-BR/Resources.resw index aed38b821462..664492761552 100644 --- a/src/Files/Strings/pt-BR/Resources.resw +++ b/src/Files/Strings/pt-BR/Resources.resw @@ -2524,24 +2524,6 @@ Usamos o App Center para controlar o uso do aplicativo, encontrar bugs e corrigi Criar link em {0} - - Colunas - - - Detalhes - - - Ícones Grandes - - - Ícones Médios - - - Ícones Pequenos - - - Blocos - Mostrar extensões de arquivo diff --git a/src/Files/Strings/pt-PT/Resources.resw b/src/Files/Strings/pt-PT/Resources.resw index 8fedc6039585..25a72c11944c 100644 --- a/src/Files/Strings/pt-PT/Resources.resw +++ b/src/Files/Strings/pt-PT/Resources.resw @@ -2525,24 +2525,6 @@ Nós utilizamos o App Center para acompanhar a utilização da aplicação, enco Criar ligação em {0} - - Colunas - - - Detalhes - - - Ícones Grandes - - - Ícones Médios - - - Ícones Pequenos - - - Mosaicos - Mostrar extensões dos ficheiros @@ -2628,7 +2610,7 @@ Nós utilizamos o App Center para acompanhar a utilização da aplicação, enco Abrir pastas com um clique único - Localização da Pasta + Localização da pasta Exportar Definições @@ -2640,7 +2622,7 @@ Nós utilizamos o App Center para acompanhar a utilização da aplicação, enco Gerir Definições - Não pudemos importas as definições. O ficheiro das definições está corrompido. + Não pudemos importar as definições. O ficheiro das definições está corrompido. Erro ao importar as definições @@ -2735,4 +2717,34 @@ Nós utilizamos o App Center para acompanhar a utilização da aplicação, enco Utilizar as definições de DPI do monitor principal + + Gestor de ficheiros predefinido + + + Definir Files como gestor de ficheiros predefinido + + + Esvaziar Reciclagem + + + Barra lateral + + + Ficheiros e pastas + + + Abrir nova instância ao abrir diretórios na jumplist da barra de tarefas + + + Definições de inicialização + + + Licenças de terceiros + + + Esta definição modifica os ficheiros do sistema e pode ter efeitos secundários inesperados no seu dispositivo. Os programadores não assumem qualquer responsabilidade no caso de ocorrer um problema como resultado. A continuação desta opção é um reconhecimento dos riscos envolvidos com esta ação. Note que a desinstalação de Files não desfaz estas alterações e pode impedi-lo de abrir o explorador de ficheiros, a menos que desligue esta definição antes de remover o Files do seu dispositivo. + + + Limpar tudo + \ No newline at end of file diff --git a/src/Files/Strings/ru-RU/Resources.resw b/src/Files/Strings/ru-RU/Resources.resw index ba079feff9c9..f71b48d0b372 100644 --- a/src/Files/Strings/ru-RU/Resources.resw +++ b/src/Files/Strings/ru-RU/Resources.resw @@ -2495,24 +2495,6 @@ Files не собирает, хранит, распространяет или Создать ссылку в {0} - - Столбцы - - - Таблица - - - Огромные значки - - - Крупные значки - - - Обычные значки - - - Плитка - Показать расширения файлов diff --git a/src/Files/Strings/zh-Hans/Resources.resw b/src/Files/Strings/zh-Hans/Resources.resw index ac2ec65cf760..e38b1c68a509 100644 --- a/src/Files/Strings/zh-Hans/Resources.resw +++ b/src/Files/Strings/zh-Hans/Resources.resw @@ -2527,24 +2527,6 @@ Files 不收集、存储、共享或发布任何个人信息。 在 {0} 处创建链接 - - 列视图 - - - 详细信息 - - - 网格视图(大) - - - 网格视图(中) - - - 网格视图(小) - - - 平铺视图 - 显示文件扩展名 @@ -2737,4 +2719,34 @@ Files 不收集、存储、共享或发布任何个人信息。 使用主屏幕的DPI设置 + + 默认文件管理器 + + + 设置Files作为默认文件管理器 + + + 清空回收站 + + + 侧边栏 + + + 文件和文件夹 + + + 从任务栏快捷列表打开目录时,启动新实例 + + + 启动设置 + + + 第三方许可 + + + 此设置会修改系统文件,在你的设备上可能引起一些未知的副作用。如果出现问题,开发人员不承担任何责任。继续使用此设置表示承认此风险。请注意,卸载Files前请关闭此设置,否则,Files无法撤销这些修改,会导致无法打开文件管理器。 + + + 清空 + \ No newline at end of file diff --git a/src/Files/Strings/zh-Hant/Resources.resw b/src/Files/Strings/zh-Hant/Resources.resw index d6b0d120e3ed..b29f23e93d25 100644 --- a/src/Files/Strings/zh-Hant/Resources.resw +++ b/src/Files/Strings/zh-Hant/Resources.resw @@ -2524,24 +2524,6 @@ Files 不會收集、存儲、共享或發布任何使用者的個資。 建立捷徑至 {0} - - 清單 - - - 詳細資料 - - - 大圖示 - - - 中圖示 - - - 小圖示 - - - 並排 - 顯示檔案副檔名 From ec319cf9fa0f6db155dcf0c1f6d8166a7fd9c1e7 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Fri, 26 Nov 2021 00:02:07 +0100 Subject: [PATCH 02/56] Fix SharpZipLib project --- .../ICSharpCode.SharpZipLib.csproj | 12 ++++++------ .../assets/ICSharpCode.SharpZipLib.snk | Bin 0 -> 596 bytes .../assets/sharpziplib-nuget-256x256.png | Bin 0 -> 5435 bytes 3 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 ICSharpCode.SharpZipLib/assets/ICSharpCode.SharpZipLib.snk create mode 100644 ICSharpCode.SharpZipLib/assets/sharpziplib-nuget-256x256.png diff --git a/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj b/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj index 066c4fb43346..7e2adffd5971 100644 --- a/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj +++ b/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj @@ -3,7 +3,7 @@ netstandard2.0;netstandard2.1;net45 True - ../../assets/ICSharpCode.SharpZipLib.snk + assets/ICSharpCode.SharpZipLib.snk true true $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb @@ -33,12 +33,12 @@ Please see https://github.com/icsharpcode/SharpZipLib/wiki/Release-1.3.3 for mor - - - + + + True images - - + + diff --git a/ICSharpCode.SharpZipLib/assets/ICSharpCode.SharpZipLib.snk b/ICSharpCode.SharpZipLib/assets/ICSharpCode.SharpZipLib.snk new file mode 100644 index 0000000000000000000000000000000000000000..58cf194dfdb9ac183edd43614d60de5ada399419 GIT binary patch literal 596 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 B_~ZZp literal 0 HcmV?d00001 From 2de644328beb569008aca9c81fc8e2ed2bbdb458 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sun, 28 Nov 2021 16:23:38 +0100 Subject: [PATCH 03/56] Removed sharpziplib project --- ICSharpCode.SharpZipLib/AssemblyInfo.cs | 3 - ICSharpCode.SharpZipLib/BZip2/BZip2.cs | 79 - .../BZip2/BZip2Constants.cs | 117 - .../BZip2/BZip2Exception.cs | 54 - .../BZip2/BZip2InputStream.cs | 1053 ---- .../BZip2/BZip2OutputStream.cs | 2033 ------- ICSharpCode.SharpZipLib/Checksum/Adler32.cs | 163 - ICSharpCode.SharpZipLib/Checksum/BZip2Crc.cs | 171 - ICSharpCode.SharpZipLib/Checksum/Crc32.cs | 173 - .../Checksum/CrcUtilities.cs | 158 - ICSharpCode.SharpZipLib/Checksum/IChecksum.cs | 51 - .../Core/ByteOrderUtils.cs | 130 - ICSharpCode.SharpZipLib/Core/EmptyRefs.cs | 17 - .../Core/Exceptions/SharpZipBaseException.cs | 58 - .../Exceptions/StreamDecodingException.cs | 50 - .../Exceptions/StreamUnsupportedException.cs | 49 - .../UnexpectedEndOfStreamException.cs | 49 - .../Exceptions/ValueOutOfRangeException.cs | 66 - .../Core/FileSystemScanner.cs | 545 -- .../Core/INameTransform.cs | 22 - ICSharpCode.SharpZipLib/Core/IScanFilter.cs | 15 - .../Core/InvalidNameException.cs | 53 - ICSharpCode.SharpZipLib/Core/NameFilter.cs | 284 - ICSharpCode.SharpZipLib/Core/PathFilter.cs | 318 -- ICSharpCode.SharpZipLib/Core/PathUtils.cs | 54 - ICSharpCode.SharpZipLib/Core/StreamUtils.cs | 295 - .../Encryption/PkzipClassic.cs | 487 -- .../Encryption/ZipAESStream.cs | 230 - .../Encryption/ZipAESTransform.cs | 224 - ICSharpCode.SharpZipLib/GZip/GZip.cs | 92 - ICSharpCode.SharpZipLib/GZip/GZipConstants.cs | 78 - ICSharpCode.SharpZipLib/GZip/GZipException.cs | 54 - .../GZip/GzipInputStream.cs | 361 -- .../GZip/GzipOutputStream.cs | 293 - .../ICSharpCode.SharpZipLib.csproj | 44 - ICSharpCode.SharpZipLib/Lzw/LzwConstants.cs | 63 - ICSharpCode.SharpZipLib/Lzw/LzwException.cs | 54 - ICSharpCode.SharpZipLib/Lzw/LzwInputStream.cs | 572 -- .../Tar/InvalidHeaderException.cs | 55 - ICSharpCode.SharpZipLib/Tar/TarArchive.cs | 1028 ---- ICSharpCode.SharpZipLib/Tar/TarBuffer.cs | 599 -- ICSharpCode.SharpZipLib/Tar/TarEntry.cs | 598 -- ICSharpCode.SharpZipLib/Tar/TarException.cs | 54 - .../Tar/TarExtendedHeaderReader.cs | 99 - ICSharpCode.SharpZipLib/Tar/TarHeader.cs | 1310 ----- ICSharpCode.SharpZipLib/Tar/TarInputStream.cs | 771 --- .../Tar/TarOutputStream.cs | 522 -- .../Zip/Compression/Deflater.cs | 604 -- .../Zip/Compression/DeflaterConstants.cs | 146 - .../Zip/Compression/DeflaterEngine.cs | 946 --- .../Zip/Compression/DeflaterHuffman.cs | 959 ---- .../Zip/Compression/DeflaterPending.cs | 17 - .../Zip/Compression/Inflater.cs | 887 --- .../Zip/Compression/InflaterDynHeader.cs | 151 - .../Zip/Compression/InflaterHuffmanTree.cs | 237 - .../Zip/Compression/PendingBuffer.cs | 268 - .../Streams/DeflaterOutputStream.cs | 513 -- .../Streams/InflaterInputStream.cs | 713 --- .../Zip/Compression/Streams/OutputWindow.cs | 220 - .../Compression/Streams/StreamManipulator.cs | 298 - ICSharpCode.SharpZipLib/Zip/FastZip.cs | 1003 ---- ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs | 67 - .../Zip/WindowsNameTransform.cs | 266 - ICSharpCode.SharpZipLib/Zip/ZipConstants.cs | 475 -- .../Zip/ZipEncryptionMethod.cs | 28 - ICSharpCode.SharpZipLib/Zip/ZipEntry.cs | 1157 ---- .../Zip/ZipEntryExtensions.cs | 32 - .../Zip/ZipEntryFactory.cs | 375 -- ICSharpCode.SharpZipLib/Zip/ZipException.cs | 54 - ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs | 974 ---- ICSharpCode.SharpZipLib/Zip/ZipFile.cs | 5059 ----------------- ICSharpCode.SharpZipLib/Zip/ZipFormat.cs | 597 -- .../Zip/ZipHelperStream.cs | 0 ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs | 730 --- .../Zip/ZipNameTransform.cs | 313 - .../Zip/ZipOutputStream.cs | 1004 ---- ICSharpCode.SharpZipLib/Zip/ZipStrings.cs | 213 - .../assets/ICSharpCode.SharpZipLib.snk | Bin 596 -> 0 bytes .../assets/sharpziplib-nuget-256x256.png | Bin 5435 -> 0 bytes src/Files/Files.csproj | 4 - 80 files changed, 31958 deletions(-) delete mode 100644 ICSharpCode.SharpZipLib/AssemblyInfo.cs delete mode 100644 ICSharpCode.SharpZipLib/BZip2/BZip2.cs delete mode 100644 ICSharpCode.SharpZipLib/BZip2/BZip2Constants.cs delete mode 100644 ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs delete mode 100644 ICSharpCode.SharpZipLib/BZip2/BZip2InputStream.cs delete mode 100644 ICSharpCode.SharpZipLib/BZip2/BZip2OutputStream.cs delete mode 100644 ICSharpCode.SharpZipLib/Checksum/Adler32.cs delete mode 100644 ICSharpCode.SharpZipLib/Checksum/BZip2Crc.cs delete mode 100644 ICSharpCode.SharpZipLib/Checksum/Crc32.cs delete mode 100644 ICSharpCode.SharpZipLib/Checksum/CrcUtilities.cs delete mode 100644 ICSharpCode.SharpZipLib/Checksum/IChecksum.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/EmptyRefs.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/FileSystemScanner.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/INameTransform.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/IScanFilter.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/InvalidNameException.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/NameFilter.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/PathFilter.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/PathUtils.cs delete mode 100644 ICSharpCode.SharpZipLib/Core/StreamUtils.cs delete mode 100644 ICSharpCode.SharpZipLib/Encryption/PkzipClassic.cs delete mode 100644 ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs delete mode 100644 ICSharpCode.SharpZipLib/Encryption/ZipAESTransform.cs delete mode 100644 ICSharpCode.SharpZipLib/GZip/GZip.cs delete mode 100644 ICSharpCode.SharpZipLib/GZip/GZipConstants.cs delete mode 100644 ICSharpCode.SharpZipLib/GZip/GZipException.cs delete mode 100644 ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs delete mode 100644 ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs delete mode 100644 ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj delete mode 100644 ICSharpCode.SharpZipLib/Lzw/LzwConstants.cs delete mode 100644 ICSharpCode.SharpZipLib/Lzw/LzwException.cs delete mode 100644 ICSharpCode.SharpZipLib/Lzw/LzwInputStream.cs delete mode 100644 ICSharpCode.SharpZipLib/Tar/InvalidHeaderException.cs delete mode 100644 ICSharpCode.SharpZipLib/Tar/TarArchive.cs delete mode 100644 ICSharpCode.SharpZipLib/Tar/TarBuffer.cs delete mode 100644 ICSharpCode.SharpZipLib/Tar/TarEntry.cs delete mode 100644 ICSharpCode.SharpZipLib/Tar/TarException.cs delete mode 100644 ICSharpCode.SharpZipLib/Tar/TarExtendedHeaderReader.cs delete mode 100644 ICSharpCode.SharpZipLib/Tar/TarHeader.cs delete mode 100644 ICSharpCode.SharpZipLib/Tar/TarInputStream.cs delete mode 100644 ICSharpCode.SharpZipLib/Tar/TarOutputStream.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Deflater.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/DeflaterConstants.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/DeflaterHuffman.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/DeflaterPending.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Inflater.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/InflaterDynHeader.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/InflaterHuffmanTree.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/PendingBuffer.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Streams/OutputWindow.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/Compression/Streams/StreamManipulator.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/FastZip.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/WindowsNameTransform.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipConstants.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipEncryptionMethod.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipEntry.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipEntryExtensions.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipException.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipFile.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipFormat.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipNameTransform.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs delete mode 100644 ICSharpCode.SharpZipLib/Zip/ZipStrings.cs delete mode 100644 ICSharpCode.SharpZipLib/assets/ICSharpCode.SharpZipLib.snk delete mode 100644 ICSharpCode.SharpZipLib/assets/sharpziplib-nuget-256x256.png diff --git a/ICSharpCode.SharpZipLib/AssemblyInfo.cs b/ICSharpCode.SharpZipLib/AssemblyInfo.cs deleted file mode 100644 index 8f8e620164ac..000000000000 --- a/ICSharpCode.SharpZipLib/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("ICSharpCode.SharpZipLib.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b9a14ea8fc9d7599e0e82a1292a23103f0210e2f928a0f466963af23fffadba59dcc8c9e26ecd114d7c0b4179e4bc93b1656b7ee2d4a67dd7c1992653e0d9cc534f7914b6f583b022e0a7aa8a430f407932f9a6806f0fc64d61e78d5ae01aa8f8233196719d44da2c50a2d1cfa3f7abb7487b3567a4f0456aa6667154c6749b1")] diff --git a/ICSharpCode.SharpZipLib/BZip2/BZip2.cs b/ICSharpCode.SharpZipLib/BZip2/BZip2.cs deleted file mode 100644 index 4bd48b0357c3..000000000000 --- a/ICSharpCode.SharpZipLib/BZip2/BZip2.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.IO; - -namespace ICSharpCode.SharpZipLib.BZip2 -{ - /// - /// An example class to demonstrate compression and decompression of BZip2 streams. - /// - public static class BZip2 - { - /// - /// Decompress the input writing - /// uncompressed data to the output stream - /// - /// The readable stream containing data to decompress. - /// The output stream to receive the decompressed data. - /// Both streams are closed on completion if true. - public static void Decompress(Stream inStream, Stream outStream, bool isStreamOwner) - { - if (inStream == null) - throw new ArgumentNullException(nameof(inStream)); - - if (outStream == null) - throw new ArgumentNullException(nameof(outStream)); - - try - { - using (BZip2InputStream bzipInput = new BZip2InputStream(inStream)) - { - bzipInput.IsStreamOwner = isStreamOwner; - Core.StreamUtils.Copy(bzipInput, outStream, new byte[4096]); - } - } - finally - { - if (isStreamOwner) - { - // inStream is closed by the BZip2InputStream if stream owner - outStream.Dispose(); - } - } - } - - /// - /// Compress the input stream sending - /// result data to output stream - /// - /// The readable stream to compress. - /// The output stream to receive the compressed data. - /// Both streams are closed on completion if true. - /// Block size acts as compression level (1 to 9) with 1 giving - /// the lowest compression and 9 the highest. - public static void Compress(Stream inStream, Stream outStream, bool isStreamOwner, int level) - { - if (inStream == null) - throw new ArgumentNullException(nameof(inStream)); - - if (outStream == null) - throw new ArgumentNullException(nameof(outStream)); - - try - { - using (BZip2OutputStream bzipOutput = new BZip2OutputStream(outStream, level)) - { - bzipOutput.IsStreamOwner = isStreamOwner; - Core.StreamUtils.Copy(inStream, bzipOutput, new byte[4096]); - } - } - finally - { - if (isStreamOwner) - { - // outStream is closed by the BZip2OutputStream if stream owner - inStream.Dispose(); - } - } - } - } -} diff --git a/ICSharpCode.SharpZipLib/BZip2/BZip2Constants.cs b/ICSharpCode.SharpZipLib/BZip2/BZip2Constants.cs deleted file mode 100644 index 52fb8ad20f16..000000000000 --- a/ICSharpCode.SharpZipLib/BZip2/BZip2Constants.cs +++ /dev/null @@ -1,117 +0,0 @@ -namespace ICSharpCode.SharpZipLib.BZip2 -{ - /// - /// Defines internal values for both compression and decompression - /// - internal static class BZip2Constants - { - /// - /// Random numbers used to randomise repetitive blocks - /// - public readonly static int[] RandomNumbers = { - 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, - 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, - 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, - 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, - 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, - 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, - 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, - 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, - 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, - 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, - 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, - 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, - 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, - 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, - 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, - 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, - 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, - 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, - 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, - 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, - 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, - 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, - 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, - 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, - 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, - 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, - 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, - 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, - 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, - 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, - 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, - 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, - 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, - 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, - 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, - 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, - 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, - 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, - 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, - 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, - 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, - 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, - 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, - 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, - 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, - 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, - 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, - 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, - 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, - 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, - 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, - 936, 638 - }; - - /// - /// When multiplied by compression parameter (1-9) gives the block size for compression - /// 9 gives the best compression but uses the most memory. - /// - public const int BaseBlockSize = 100000; - - /// - /// Backend constant - /// - public const int MaximumAlphaSize = 258; - - /// - /// Backend constant - /// - public const int MaximumCodeLength = 23; - - /// - /// Backend constant - /// - public const int RunA = 0; - - /// - /// Backend constant - /// - public const int RunB = 1; - - /// - /// Backend constant - /// - public const int GroupCount = 6; - - /// - /// Backend constant - /// - public const int GroupSize = 50; - - /// - /// Backend constant - /// - public const int NumberOfIterations = 4; - - /// - /// Backend constant - /// - public const int MaximumSelectors = (2 + (900000 / GroupSize)); - - /// - /// Backend constant - /// - public const int OvershootBytes = 20; - } -} diff --git a/ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs b/ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs deleted file mode 100644 index 111d21cdcde0..000000000000 --- a/ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib.BZip2 -{ - /// - /// BZip2Exception represents exceptions specific to BZip2 classes and code. - /// - [Serializable] - public class BZip2Exception : SharpZipBaseException - { - /// - /// Initialise a new instance of . - /// - public BZip2Exception() - { - } - - /// - /// Initialise a new instance of with its message string. - /// - /// A that describes the error. - public BZip2Exception(string message) - : base(message) - { - } - - /// - /// Initialise a new instance of . - /// - /// A that describes the error. - /// The that caused this exception. - public BZip2Exception(string message, Exception innerException) - : base(message, innerException) - { - } - - /// - /// Initializes a new instance of the BZip2Exception class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected BZip2Exception(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/BZip2/BZip2InputStream.cs b/ICSharpCode.SharpZipLib/BZip2/BZip2InputStream.cs deleted file mode 100644 index 3948b4e4cc15..000000000000 --- a/ICSharpCode.SharpZipLib/BZip2/BZip2InputStream.cs +++ /dev/null @@ -1,1053 +0,0 @@ -#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER - #define VECTORIZE_MEMORY_MOVE -#endif - -using ICSharpCode.SharpZipLib.Checksum; -using System; -using System.IO; - -namespace ICSharpCode.SharpZipLib.BZip2 -{ - /// - /// An input stream that decompresses files in the BZip2 format - /// - public class BZip2InputStream : Stream - { - #region Constants - - private const int START_BLOCK_STATE = 1; - private const int RAND_PART_A_STATE = 2; - private const int RAND_PART_B_STATE = 3; - private const int RAND_PART_C_STATE = 4; - private const int NO_RAND_PART_A_STATE = 5; - private const int NO_RAND_PART_B_STATE = 6; - private const int NO_RAND_PART_C_STATE = 7; - -#if VECTORIZE_MEMORY_MOVE - private static readonly int VectorSize = System.Numerics.Vector.Count; -#endif // VECTORIZE_MEMORY_MOVE - -#endregion Constants - - #region Instance Fields - - /*-- - index of the last char in the block, so - the block size == last + 1. - --*/ - private int last; - - /*-- - index in zptr[] of original string after sorting. - --*/ - private int origPtr; - - /*-- - always: in the range 0 .. 9. - The current block size is 100000 * this number. - --*/ - private int blockSize100k; - - private bool blockRandomised; - - private int bsBuff; - private int bsLive; - private IChecksum mCrc = new BZip2Crc(); - - private bool[] inUse = new bool[256]; - private int nInUse; - - private byte[] seqToUnseq = new byte[256]; - private byte[] unseqToSeq = new byte[256]; - - private byte[] selector = new byte[BZip2Constants.MaximumSelectors]; - private byte[] selectorMtf = new byte[BZip2Constants.MaximumSelectors]; - - private int[] tt; - private byte[] ll8; - - /*-- - freq table collected to save a pass over the data - during decompression. - --*/ - private int[] unzftab = new int[256]; - - private int[][] limit = new int[BZip2Constants.GroupCount][]; - private int[][] baseArray = new int[BZip2Constants.GroupCount][]; - private int[][] perm = new int[BZip2Constants.GroupCount][]; - private int[] minLens = new int[BZip2Constants.GroupCount]; - - private readonly Stream baseStream; - private bool streamEnd; - - private int currentChar = -1; - - private int currentState = START_BLOCK_STATE; - - private int storedBlockCRC, storedCombinedCRC; - private int computedBlockCRC; - private uint computedCombinedCRC; - - private int count, chPrev, ch2; - private int tPos; - private int rNToGo; - private int rTPos; - private int i2, j2; - private byte z; - - #endregion Instance Fields - - /// - /// Construct instance for reading from stream - /// - /// Data source - public BZip2InputStream(Stream stream) - { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - // init arrays - for (int i = 0; i < BZip2Constants.GroupCount; ++i) - { - limit[i] = new int[BZip2Constants.MaximumAlphaSize]; - baseArray[i] = new int[BZip2Constants.MaximumAlphaSize]; - perm[i] = new int[BZip2Constants.MaximumAlphaSize]; - } - - baseStream = stream; - bsLive = 0; - bsBuff = 0; - Initialize(); - InitBlock(); - SetupBlock(); - } - - /// - /// Get/set flag indicating ownership of underlying stream. - /// When the flag is true will close the underlying stream also. - /// - public bool IsStreamOwner { get; set; } = true; - - #region Stream Overrides - - /// - /// Gets a value indicating if the stream supports reading - /// - public override bool CanRead - { - get - { - return baseStream.CanRead; - } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - public override bool CanSeek - { - get - { - return false; - } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// This property always returns false - /// - public override bool CanWrite - { - get - { - return false; - } - } - - /// - /// Gets the length in bytes of the stream. - /// - public override long Length - { - get - { - return baseStream.Length; - } - } - - /// - /// Gets the current position of the stream. - /// Setting the position is not supported and will throw a NotSupportException. - /// - /// Any attempt to set the position. - public override long Position - { - get - { - return baseStream.Position; - } - set - { - throw new NotSupportedException("BZip2InputStream position cannot be set"); - } - } - - /// - /// Flushes the stream. - /// - public override void Flush() - { - baseStream.Flush(); - } - - /// - /// Set the streams position. This operation is not supported and will throw a NotSupportedException - /// - /// A byte offset relative to the parameter. - /// A value of type indicating the reference point used to obtain the new position. - /// The new position of the stream. - /// Any access - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException("BZip2InputStream Seek not supported"); - } - - /// - /// Sets the length of this stream to the given value. - /// This operation is not supported and will throw a NotSupportedExceptionortedException - /// - /// The new length for the stream. - /// Any access - public override void SetLength(long value) - { - throw new NotSupportedException("BZip2InputStream SetLength not supported"); - } - - /// - /// Writes a block of bytes to this stream using data from a buffer. - /// This operation is not supported and will throw a NotSupportedException - /// - /// The buffer to source data from. - /// The offset to start obtaining data from. - /// The number of bytes of data to write. - /// Any access - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException("BZip2InputStream Write not supported"); - } - - /// - /// Writes a byte to the current position in the file stream. - /// This operation is not supported and will throw a NotSupportedException - /// - /// The value to write. - /// Any access - public override void WriteByte(byte value) - { - throw new NotSupportedException("BZip2InputStream WriteByte not supported"); - } - - /// - /// Read a sequence of bytes and advances the read position by one byte. - /// - /// Array of bytes to store values in - /// Offset in array to begin storing data - /// The maximum number of bytes to read - /// The total number of bytes read into the buffer. This might be less - /// than the number of bytes requested if that number of bytes are not - /// currently available or zero if the end of the stream is reached. - /// - public override int Read(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - for (int i = 0; i < count; ++i) - { - int rb = ReadByte(); - if (rb == -1) - { - return i; - } - buffer[offset + i] = (byte)rb; - } - return count; - } - - /// - /// Closes the stream, releasing any associated resources. - /// - protected override void Dispose(bool disposing) - { - if (disposing && IsStreamOwner) - { - baseStream.Dispose(); - } - } - - /// - /// Read a byte from stream advancing position - /// - /// byte read or -1 on end of stream - public override int ReadByte() - { - if (streamEnd) - { - return -1; // ok - } - - int retChar = currentChar; - switch (currentState) - { - case RAND_PART_B_STATE: - SetupRandPartB(); - break; - - case RAND_PART_C_STATE: - SetupRandPartC(); - break; - - case NO_RAND_PART_B_STATE: - SetupNoRandPartB(); - break; - - case NO_RAND_PART_C_STATE: - SetupNoRandPartC(); - break; - - case START_BLOCK_STATE: - case NO_RAND_PART_A_STATE: - case RAND_PART_A_STATE: - break; - } - return retChar; - } - - #endregion Stream Overrides - - private void MakeMaps() - { - nInUse = 0; - for (int i = 0; i < 256; ++i) - { - if (inUse[i]) - { - seqToUnseq[nInUse] = (byte)i; - unseqToSeq[i] = (byte)nInUse; - nInUse++; - } - } - } - - private void Initialize() - { - char magic1 = BsGetUChar(); - char magic2 = BsGetUChar(); - - char magic3 = BsGetUChar(); - char magic4 = BsGetUChar(); - - if (magic1 != 'B' || magic2 != 'Z' || magic3 != 'h' || magic4 < '1' || magic4 > '9') - { - streamEnd = true; - return; - } - - SetDecompressStructureSizes(magic4 - '0'); - computedCombinedCRC = 0; - } - - private void InitBlock() - { - char magic1 = BsGetUChar(); - char magic2 = BsGetUChar(); - char magic3 = BsGetUChar(); - char magic4 = BsGetUChar(); - char magic5 = BsGetUChar(); - char magic6 = BsGetUChar(); - - if (magic1 == 0x17 && magic2 == 0x72 && magic3 == 0x45 && magic4 == 0x38 && magic5 == 0x50 && magic6 == 0x90) - { - Complete(); - return; - } - - if (magic1 != 0x31 || magic2 != 0x41 || magic3 != 0x59 || magic4 != 0x26 || magic5 != 0x53 || magic6 != 0x59) - { - BadBlockHeader(); - streamEnd = true; - return; - } - - storedBlockCRC = BsGetInt32(); - - blockRandomised = (BsR(1) == 1); - - GetAndMoveToFrontDecode(); - - mCrc.Reset(); - currentState = START_BLOCK_STATE; - } - - private void EndBlock() - { - computedBlockCRC = (int)mCrc.Value; - - // -- A bad CRC is considered a fatal error. -- - if (storedBlockCRC != computedBlockCRC) - { - CrcError(); - } - - // 1528150659 - computedCombinedCRC = ((computedCombinedCRC << 1) & 0xFFFFFFFF) | (computedCombinedCRC >> 31); - computedCombinedCRC = computedCombinedCRC ^ (uint)computedBlockCRC; - } - - private void Complete() - { - storedCombinedCRC = BsGetInt32(); - if (storedCombinedCRC != (int)computedCombinedCRC) - { - CrcError(); - } - - streamEnd = true; - } - - private void FillBuffer() - { - int thech = 0; - - try - { - thech = baseStream.ReadByte(); - } - catch (Exception) - { - CompressedStreamEOF(); - } - - if (thech == -1) - { - CompressedStreamEOF(); - } - - bsBuff = (bsBuff << 8) | (thech & 0xFF); - bsLive += 8; - } - - private int BsR(int n) - { - while (bsLive < n) - { - FillBuffer(); - } - - int v = (bsBuff >> (bsLive - n)) & ((1 << n) - 1); - bsLive -= n; - return v; - } - - private char BsGetUChar() - { - return (char)BsR(8); - } - - private int BsGetIntVS(int numBits) - { - return BsR(numBits); - } - - private int BsGetInt32() - { - int result = BsR(8); - result = (result << 8) | BsR(8); - result = (result << 8) | BsR(8); - result = (result << 8) | BsR(8); - return result; - } - - private void RecvDecodingTables() - { - char[][] len = new char[BZip2Constants.GroupCount][]; - for (int i = 0; i < BZip2Constants.GroupCount; ++i) - { - len[i] = new char[BZip2Constants.MaximumAlphaSize]; - } - - bool[] inUse16 = new bool[16]; - - //--- Receive the mapping table --- - for (int i = 0; i < 16; i++) - { - inUse16[i] = (BsR(1) == 1); - } - - for (int i = 0; i < 16; i++) - { - if (inUse16[i]) - { - for (int j = 0; j < 16; j++) - { - inUse[i * 16 + j] = (BsR(1) == 1); - } - } - else - { - for (int j = 0; j < 16; j++) - { - inUse[i * 16 + j] = false; - } - } - } - - MakeMaps(); - int alphaSize = nInUse + 2; - - //--- Now the selectors --- - int nGroups = BsR(3); - int nSelectors = BsR(15); - - for (int i = 0; i < nSelectors; i++) - { - int j = 0; - while (BsR(1) == 1) - { - j++; - } - selectorMtf[i] = (byte)j; - } - - //--- Undo the MTF values for the selectors. --- - byte[] pos = new byte[BZip2Constants.GroupCount]; - for (int v = 0; v < nGroups; v++) - { - pos[v] = (byte)v; - } - - for (int i = 0; i < nSelectors; i++) - { - int v = selectorMtf[i]; - byte tmp = pos[v]; - while (v > 0) - { - pos[v] = pos[v - 1]; - v--; - } - pos[0] = tmp; - selector[i] = tmp; - } - - //--- Now the coding tables --- - for (int t = 0; t < nGroups; t++) - { - int curr = BsR(5); - for (int i = 0; i < alphaSize; i++) - { - while (BsR(1) == 1) - { - if (BsR(1) == 0) - { - curr++; - } - else - { - curr--; - } - } - len[t][i] = (char)curr; - } - } - - //--- Create the Huffman decoding tables --- - for (int t = 0; t < nGroups; t++) - { - int minLen = 32; - int maxLen = 0; - for (int i = 0; i < alphaSize; i++) - { - maxLen = Math.Max(maxLen, len[t][i]); - minLen = Math.Min(minLen, len[t][i]); - } - HbCreateDecodeTables(limit[t], baseArray[t], perm[t], len[t], minLen, maxLen, alphaSize); - minLens[t] = minLen; - } - } - - private void GetAndMoveToFrontDecode() - { - byte[] yy = new byte[256]; - int nextSym; - - int limitLast = BZip2Constants.BaseBlockSize * blockSize100k; - origPtr = BsGetIntVS(24); - - RecvDecodingTables(); - int EOB = nInUse + 1; - int groupNo = -1; - int groupPos = 0; - - /*-- - Setting up the unzftab entries here is not strictly - necessary, but it does save having to do it later - in a separate pass, and so saves a block's worth of - cache misses. - --*/ - for (int i = 0; i <= 255; i++) - { - unzftab[i] = 0; - } - - for (int i = 0; i <= 255; i++) - { - yy[i] = (byte)i; - } - - last = -1; - - if (groupPos == 0) - { - groupNo++; - groupPos = BZip2Constants.GroupSize; - } - - groupPos--; - int zt = selector[groupNo]; - int zn = minLens[zt]; - int zvec = BsR(zn); - int zj; - - while (zvec > limit[zt][zn]) - { - if (zn > 20) - { // the longest code - throw new BZip2Exception("Bzip data error"); - } - zn++; - while (bsLive < 1) - { - FillBuffer(); - } - zj = (bsBuff >> (bsLive - 1)) & 1; - bsLive--; - zvec = (zvec << 1) | zj; - } - if (zvec - baseArray[zt][zn] < 0 || zvec - baseArray[zt][zn] >= BZip2Constants.MaximumAlphaSize) - { - throw new BZip2Exception("Bzip data error"); - } - nextSym = perm[zt][zvec - baseArray[zt][zn]]; - - while (true) - { - if (nextSym == EOB) - { - break; - } - - if (nextSym == BZip2Constants.RunA || nextSym == BZip2Constants.RunB) - { - int s = -1; - int n = 1; - do - { - if (nextSym == BZip2Constants.RunA) - { - s += (0 + 1) * n; - } - else if (nextSym == BZip2Constants.RunB) - { - s += (1 + 1) * n; - } - - n <<= 1; - - if (groupPos == 0) - { - groupNo++; - groupPos = BZip2Constants.GroupSize; - } - - groupPos--; - - zt = selector[groupNo]; - zn = minLens[zt]; - zvec = BsR(zn); - - while (zvec > limit[zt][zn]) - { - zn++; - while (bsLive < 1) - { - FillBuffer(); - } - zj = (bsBuff >> (bsLive - 1)) & 1; - bsLive--; - zvec = (zvec << 1) | zj; - } - nextSym = perm[zt][zvec - baseArray[zt][zn]]; - } while (nextSym == BZip2Constants.RunA || nextSym == BZip2Constants.RunB); - - s++; - byte ch = seqToUnseq[yy[0]]; - unzftab[ch] += s; - - while (s > 0) - { - last++; - ll8[last] = ch; - s--; - } - - if (last >= limitLast) - { - BlockOverrun(); - } - continue; - } - else - { - last++; - if (last >= limitLast) - { - BlockOverrun(); - } - - byte tmp = yy[nextSym - 1]; - unzftab[seqToUnseq[tmp]]++; - ll8[last] = seqToUnseq[tmp]; - - var j = nextSym - 1; - -#if VECTORIZE_MEMORY_MOVE - // This is vectorized memory move. Going from the back, we're taking chunks of array - // and write them at the new location shifted by one. Since chunks are VectorSize long, - // at the end we have to move "tail" (or head actually) of the array using a plain loop. - // If System.Numerics.Vector API is not available, the plain loop is used to do the whole copying. - - while(j >= VectorSize) - { - var arrayPart = new System.Numerics.Vector(yy, j - VectorSize); - arrayPart.CopyTo(yy, j - VectorSize + 1); - j -= VectorSize; - } -#endif // VECTORIZE_MEMORY_MOVE - - while(j > 0) - { - yy[j] = yy[--j]; - } - - yy[0] = tmp; - - if (groupPos == 0) - { - groupNo++; - groupPos = BZip2Constants.GroupSize; - } - - groupPos--; - zt = selector[groupNo]; - zn = minLens[zt]; - zvec = BsR(zn); - while (zvec > limit[zt][zn]) - { - zn++; - while (bsLive < 1) - { - FillBuffer(); - } - zj = (bsBuff >> (bsLive - 1)) & 1; - bsLive--; - zvec = (zvec << 1) | zj; - } - nextSym = perm[zt][zvec - baseArray[zt][zn]]; - continue; - } - } - } - - private void SetupBlock() - { - int[] cftab = new int[257]; - - cftab[0] = 0; - Array.Copy(unzftab, 0, cftab, 1, 256); - - for (int i = 1; i <= 256; i++) - { - cftab[i] += cftab[i - 1]; - } - - for (int i = 0; i <= last; i++) - { - byte ch = ll8[i]; - tt[cftab[ch]] = i; - cftab[ch]++; - } - - cftab = null; - - tPos = tt[origPtr]; - - count = 0; - i2 = 0; - ch2 = 256; /*-- not a char and not EOF --*/ - - if (blockRandomised) - { - rNToGo = 0; - rTPos = 0; - SetupRandPartA(); - } - else - { - SetupNoRandPartA(); - } - } - - private void SetupRandPartA() - { - if (i2 <= last) - { - chPrev = ch2; - ch2 = ll8[tPos]; - tPos = tt[tPos]; - if (rNToGo == 0) - { - rNToGo = BZip2Constants.RandomNumbers[rTPos]; - rTPos++; - if (rTPos == 512) - { - rTPos = 0; - } - } - rNToGo--; - ch2 ^= (int)((rNToGo == 1) ? 1 : 0); - i2++; - - currentChar = ch2; - currentState = RAND_PART_B_STATE; - mCrc.Update(ch2); - } - else - { - EndBlock(); - InitBlock(); - SetupBlock(); - } - } - - private void SetupNoRandPartA() - { - if (i2 <= last) - { - chPrev = ch2; - ch2 = ll8[tPos]; - tPos = tt[tPos]; - i2++; - - currentChar = ch2; - currentState = NO_RAND_PART_B_STATE; - mCrc.Update(ch2); - } - else - { - EndBlock(); - InitBlock(); - SetupBlock(); - } - } - - private void SetupRandPartB() - { - if (ch2 != chPrev) - { - currentState = RAND_PART_A_STATE; - count = 1; - SetupRandPartA(); - } - else - { - count++; - if (count >= 4) - { - z = ll8[tPos]; - tPos = tt[tPos]; - if (rNToGo == 0) - { - rNToGo = BZip2Constants.RandomNumbers[rTPos]; - rTPos++; - if (rTPos == 512) - { - rTPos = 0; - } - } - rNToGo--; - z ^= (byte)((rNToGo == 1) ? 1 : 0); - j2 = 0; - currentState = RAND_PART_C_STATE; - SetupRandPartC(); - } - else - { - currentState = RAND_PART_A_STATE; - SetupRandPartA(); - } - } - } - - private void SetupRandPartC() - { - if (j2 < (int)z) - { - currentChar = ch2; - mCrc.Update(ch2); - j2++; - } - else - { - currentState = RAND_PART_A_STATE; - i2++; - count = 0; - SetupRandPartA(); - } - } - - private void SetupNoRandPartB() - { - if (ch2 != chPrev) - { - currentState = NO_RAND_PART_A_STATE; - count = 1; - SetupNoRandPartA(); - } - else - { - count++; - if (count >= 4) - { - z = ll8[tPos]; - tPos = tt[tPos]; - currentState = NO_RAND_PART_C_STATE; - j2 = 0; - SetupNoRandPartC(); - } - else - { - currentState = NO_RAND_PART_A_STATE; - SetupNoRandPartA(); - } - } - } - - private void SetupNoRandPartC() - { - if (j2 < (int)z) - { - currentChar = ch2; - mCrc.Update(ch2); - j2++; - } - else - { - currentState = NO_RAND_PART_A_STATE; - i2++; - count = 0; - SetupNoRandPartA(); - } - } - - private void SetDecompressStructureSizes(int newSize100k) - { - if (!(0 <= newSize100k && newSize100k <= 9 && 0 <= blockSize100k && blockSize100k <= 9)) - { - throw new BZip2Exception("Invalid block size"); - } - - blockSize100k = newSize100k; - - if (newSize100k == 0) - { - return; - } - - int n = BZip2Constants.BaseBlockSize * newSize100k; - ll8 = new byte[n]; - tt = new int[n]; - } - - private static void CompressedStreamEOF() - { - throw new EndOfStreamException("BZip2 input stream end of compressed stream"); - } - - private static void BlockOverrun() - { - throw new BZip2Exception("BZip2 input stream block overrun"); - } - - private static void BadBlockHeader() - { - throw new BZip2Exception("BZip2 input stream bad block header"); - } - - private static void CrcError() - { - throw new BZip2Exception("BZip2 input stream crc error"); - } - - private static void HbCreateDecodeTables(int[] limit, int[] baseArray, int[] perm, char[] length, int minLen, int maxLen, int alphaSize) - { - int pp = 0; - - for (int i = minLen; i <= maxLen; ++i) - { - for (int j = 0; j < alphaSize; ++j) - { - if (length[j] == i) - { - perm[pp] = j; - ++pp; - } - } - } - - for (int i = 0; i < BZip2Constants.MaximumCodeLength; i++) - { - baseArray[i] = 0; - } - - for (int i = 0; i < alphaSize; i++) - { - ++baseArray[length[i] + 1]; - } - - for (int i = 1; i < BZip2Constants.MaximumCodeLength; i++) - { - baseArray[i] += baseArray[i - 1]; - } - - for (int i = 0; i < BZip2Constants.MaximumCodeLength; i++) - { - limit[i] = 0; - } - - int vec = 0; - - for (int i = minLen; i <= maxLen; i++) - { - vec += (baseArray[i + 1] - baseArray[i]); - limit[i] = vec - 1; - vec <<= 1; - } - - for (int i = minLen + 1; i <= maxLen; i++) - { - baseArray[i] = ((limit[i - 1] + 1) << 1) - baseArray[i]; - } - } - } -} diff --git a/ICSharpCode.SharpZipLib/BZip2/BZip2OutputStream.cs b/ICSharpCode.SharpZipLib/BZip2/BZip2OutputStream.cs deleted file mode 100644 index f331ec657410..000000000000 --- a/ICSharpCode.SharpZipLib/BZip2/BZip2OutputStream.cs +++ /dev/null @@ -1,2033 +0,0 @@ -using ICSharpCode.SharpZipLib.Checksum; -using System; -using System.IO; - -namespace ICSharpCode.SharpZipLib.BZip2 -{ - /// - /// An output stream that compresses into the BZip2 format - /// including file header chars into another stream. - /// - public class BZip2OutputStream : Stream - { - #region Constants - - private const int SETMASK = (1 << 21); - private const int CLEARMASK = (~SETMASK); - private const int GREATER_ICOST = 15; - private const int LESSER_ICOST = 0; - private const int SMALL_THRESH = 20; - private const int DEPTH_THRESH = 10; - - /*-- - If you are ever unlucky/improbable enough - to get a stack overflow whilst sorting, - increase the following constant and try - again. In practice I have never seen the - stack go above 27 elems, so the following - limit seems very generous. - --*/ - private const int QSORT_STACK_SIZE = 1000; - - /*-- - Knuth's increments seem to work better - than Incerpi-Sedgewick here. Possibly - because the number of elems to sort is - usually small, typically <= 20. - --*/ - - private readonly int[] increments = { - 1, 4, 13, 40, 121, 364, 1093, 3280, - 9841, 29524, 88573, 265720, - 797161, 2391484 - }; - - #endregion Constants - - #region Instance Fields - - /*-- - index of the last char in the block, so - the block size == last + 1. - --*/ - private int last; - - /*-- - index in zptr[] of original string after sorting. - --*/ - private int origPtr; - - /*-- - always: in the range 0 .. 9. - The current block size is 100000 * this number. - --*/ - private int blockSize100k; - - private bool blockRandomised; - - private int bytesOut; - private int bsBuff; - private int bsLive; - private IChecksum mCrc = new BZip2Crc(); - - private bool[] inUse = new bool[256]; - private int nInUse; - - private char[] seqToUnseq = new char[256]; - private char[] unseqToSeq = new char[256]; - - private char[] selector = new char[BZip2Constants.MaximumSelectors]; - private char[] selectorMtf = new char[BZip2Constants.MaximumSelectors]; - - private byte[] block; - private int[] quadrant; - private int[] zptr; - private short[] szptr; - private int[] ftab; - - private int nMTF; - - private int[] mtfFreq = new int[BZip2Constants.MaximumAlphaSize]; - - /* - * Used when sorting. If too many long comparisons - * happen, we stop sorting, randomise the block - * slightly, and try again. - */ - private int workFactor; - private int workDone; - private int workLimit; - private bool firstAttempt; - private int nBlocksRandomised; - - private int currentChar = -1; - private int runLength; - private uint blockCRC, combinedCRC; - private int allowableBlockSize; - private readonly Stream baseStream; - private bool disposed_; - - #endregion Instance Fields - - /// - /// Construct a default output stream with maximum block size - /// - /// The stream to write BZip data onto. - public BZip2OutputStream(Stream stream) : this(stream, 9) - { - } - - /// - /// Initialise a new instance of the - /// for the specified stream, using the given blocksize. - /// - /// The stream to write compressed data to. - /// The block size to use. - /// - /// Valid block sizes are in the range 1..9, with 1 giving - /// the lowest compression and 9 the highest. - /// - public BZip2OutputStream(Stream stream, int blockSize) - { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - baseStream = stream; - bsLive = 0; - bsBuff = 0; - bytesOut = 0; - - workFactor = 50; - if (blockSize > 9) - { - blockSize = 9; - } - - if (blockSize < 1) - { - blockSize = 1; - } - blockSize100k = blockSize; - AllocateCompressStructures(); - Initialize(); - InitBlock(); - } - - /// - /// Ensures that resources are freed and other cleanup operations - /// are performed when the garbage collector reclaims the BZip2OutputStream. - /// - ~BZip2OutputStream() - { - Dispose(false); - } - - /// - /// Gets or sets a flag indicating ownership of underlying stream. - /// When the flag is true will close the underlying stream also. - /// - /// The default value is true. - public bool IsStreamOwner { get; set; } = true; - - /// - /// Gets a value indicating whether the current stream supports reading - /// - public override bool CanRead - { - get - { - return false; - } - } - - /// - /// Gets a value indicating whether the current stream supports seeking - /// - public override bool CanSeek - { - get - { - return false; - } - } - - /// - /// Gets a value indicating whether the current stream supports writing - /// - public override bool CanWrite - { - get - { - return baseStream.CanWrite; - } - } - - /// - /// Gets the length in bytes of the stream - /// - public override long Length - { - get - { - return baseStream.Length; - } - } - - /// - /// Gets or sets the current position of this stream. - /// - public override long Position - { - get - { - return baseStream.Position; - } - set - { - throw new NotSupportedException("BZip2OutputStream position cannot be set"); - } - } - - /// - /// Sets the current position of this stream to the given value. - /// - /// The point relative to the offset from which to being seeking. - /// The reference point from which to begin seeking. - /// The new position in the stream. - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException("BZip2OutputStream Seek not supported"); - } - - /// - /// Sets the length of this stream to the given value. - /// - /// The new stream length. - public override void SetLength(long value) - { - throw new NotSupportedException("BZip2OutputStream SetLength not supported"); - } - - /// - /// Read a byte from the stream advancing the position. - /// - /// The byte read cast to an int; -1 if end of stream. - public override int ReadByte() - { - throw new NotSupportedException("BZip2OutputStream ReadByte not supported"); - } - - /// - /// Read a block of bytes - /// - /// The buffer to read into. - /// The offset in the buffer to start storing data at. - /// The maximum number of bytes to read. - /// The total number of bytes read. This might be less than the number of bytes - /// requested if that number of bytes are not currently available, or zero - /// if the end of the stream is reached. - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotSupportedException("BZip2OutputStream Read not supported"); - } - - /// - /// Write a block of bytes to the stream - /// - /// The buffer containing data to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - public override void Write(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - if (buffer.Length - offset < count) - { - throw new ArgumentException("Offset/count out of range"); - } - - for (int i = 0; i < count; ++i) - { - WriteByte(buffer[offset + i]); - } - } - - /// - /// Write a byte to the stream. - /// - /// The byte to write to the stream. - public override void WriteByte(byte value) - { - int b = (256 + value) % 256; - if (currentChar != -1) - { - if (currentChar == b) - { - runLength++; - if (runLength > 254) - { - WriteRun(); - currentChar = -1; - runLength = 0; - } - } - else - { - WriteRun(); - runLength = 1; - currentChar = b; - } - } - else - { - currentChar = b; - runLength++; - } - } - - private void MakeMaps() - { - nInUse = 0; - for (int i = 0; i < 256; i++) - { - if (inUse[i]) - { - seqToUnseq[nInUse] = (char)i; - unseqToSeq[i] = (char)nInUse; - nInUse++; - } - } - } - - /// - /// Get the number of bytes written to output. - /// - private void WriteRun() - { - if (last < allowableBlockSize) - { - inUse[currentChar] = true; - for (int i = 0; i < runLength; i++) - { - mCrc.Update(currentChar); - } - - switch (runLength) - { - case 1: - last++; - block[last + 1] = (byte)currentChar; - break; - - case 2: - last++; - block[last + 1] = (byte)currentChar; - last++; - block[last + 1] = (byte)currentChar; - break; - - case 3: - last++; - block[last + 1] = (byte)currentChar; - last++; - block[last + 1] = (byte)currentChar; - last++; - block[last + 1] = (byte)currentChar; - break; - - default: - inUse[runLength - 4] = true; - last++; - block[last + 1] = (byte)currentChar; - last++; - block[last + 1] = (byte)currentChar; - last++; - block[last + 1] = (byte)currentChar; - last++; - block[last + 1] = (byte)currentChar; - last++; - block[last + 1] = (byte)(runLength - 4); - break; - } - } - else - { - EndBlock(); - InitBlock(); - WriteRun(); - } - } - - /// - /// Get the number of bytes written to the output. - /// - public int BytesWritten - { - get { return bytesOut; } - } - - /// - /// Releases the unmanaged resources used by the and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - override protected void Dispose(bool disposing) - { - try - { - try - { - base.Dispose(disposing); - if (!disposed_) - { - disposed_ = true; - - if (runLength > 0) - { - WriteRun(); - } - - currentChar = -1; - EndBlock(); - EndCompression(); - Flush(); - } - } - finally - { - if (disposing) - { - if (IsStreamOwner) - { - baseStream.Dispose(); - } - } - } - } - catch - { - } - } - - /// - /// Flush output buffers - /// - public override void Flush() - { - baseStream.Flush(); - } - - private void Initialize() - { - bytesOut = 0; - nBlocksRandomised = 0; - - /*--- Write header `magic' bytes indicating file-format == huffmanised, - followed by a digit indicating blockSize100k. - ---*/ - - BsPutUChar('B'); - BsPutUChar('Z'); - - BsPutUChar('h'); - BsPutUChar('0' + blockSize100k); - - combinedCRC = 0; - } - - private void InitBlock() - { - mCrc.Reset(); - last = -1; - - for (int i = 0; i < 256; i++) - { - inUse[i] = false; - } - - /*--- 20 is just a paranoia constant ---*/ - allowableBlockSize = BZip2Constants.BaseBlockSize * blockSize100k - 20; - } - - private void EndBlock() - { - if (last < 0) - { // dont do anything for empty files, (makes empty files compatible with original Bzip) - return; - } - - blockCRC = unchecked((uint)mCrc.Value); - combinedCRC = (combinedCRC << 1) | (combinedCRC >> 31); - combinedCRC ^= blockCRC; - - /*-- sort the block and establish position of original string --*/ - DoReversibleTransformation(); - - /*-- - A 6-byte block header, the value chosen arbitrarily - as 0x314159265359 :-). A 32 bit value does not really - give a strong enough guarantee that the value will not - appear by chance in the compressed datastream. Worst-case - probability of this event, for a 900k block, is about - 2.0e-3 for 32 bits, 1.0e-5 for 40 bits and 4.0e-8 for 48 bits. - For a compressed file of size 100Gb -- about 100000 blocks -- - only a 48-bit marker will do. NB: normal compression/ - decompression do *not* rely on these statistical properties. - They are only important when trying to recover blocks from - damaged files. - --*/ - BsPutUChar(0x31); - BsPutUChar(0x41); - BsPutUChar(0x59); - BsPutUChar(0x26); - BsPutUChar(0x53); - BsPutUChar(0x59); - - /*-- Now the block's CRC, so it is in a known place. --*/ - unchecked - { - BsPutint((int)blockCRC); - } - - /*-- Now a single bit indicating randomisation. --*/ - if (blockRandomised) - { - BsW(1, 1); - nBlocksRandomised++; - } - else - { - BsW(1, 0); - } - - /*-- Finally, block's contents proper. --*/ - MoveToFrontCodeAndSend(); - } - - private void EndCompression() - { - /*-- - Now another magic 48-bit number, 0x177245385090, to - indicate the end of the last block. (sqrt(pi), if - you want to know. I did want to use e, but it contains - too much repetition -- 27 18 28 18 28 46 -- for me - to feel statistically comfortable. Call me paranoid.) - --*/ - BsPutUChar(0x17); - BsPutUChar(0x72); - BsPutUChar(0x45); - BsPutUChar(0x38); - BsPutUChar(0x50); - BsPutUChar(0x90); - - unchecked - { - BsPutint((int)combinedCRC); - } - - BsFinishedWithStream(); - } - - private void BsFinishedWithStream() - { - while (bsLive > 0) - { - int ch = (bsBuff >> 24); - baseStream.WriteByte((byte)ch); // write 8-bit - bsBuff <<= 8; - bsLive -= 8; - bytesOut++; - } - } - - private void BsW(int n, int v) - { - while (bsLive >= 8) - { - int ch = (bsBuff >> 24); - unchecked { baseStream.WriteByte((byte)ch); } // write 8-bit - bsBuff <<= 8; - bsLive -= 8; - ++bytesOut; - } - bsBuff |= (v << (32 - bsLive - n)); - bsLive += n; - } - - private void BsPutUChar(int c) - { - BsW(8, c); - } - - private void BsPutint(int u) - { - BsW(8, (u >> 24) & 0xFF); - BsW(8, (u >> 16) & 0xFF); - BsW(8, (u >> 8) & 0xFF); - BsW(8, u & 0xFF); - } - - private void BsPutIntVS(int numBits, int c) - { - BsW(numBits, c); - } - - private void SendMTFValues() - { - char[][] len = new char[BZip2Constants.GroupCount][]; - for (int i = 0; i < BZip2Constants.GroupCount; ++i) - { - len[i] = new char[BZip2Constants.MaximumAlphaSize]; - } - - int gs, ge, totc, bt, bc, iter; - int nSelectors = 0, alphaSize, minLen, maxLen, selCtr; - int nGroups; - - alphaSize = nInUse + 2; - for (int t = 0; t < BZip2Constants.GroupCount; t++) - { - for (int v = 0; v < alphaSize; v++) - { - len[t][v] = (char)GREATER_ICOST; - } - } - - /*--- Decide how many coding tables to use ---*/ - if (nMTF <= 0) - { - Panic(); - } - - if (nMTF < 200) - { - nGroups = 2; - } - else if (nMTF < 600) - { - nGroups = 3; - } - else if (nMTF < 1200) - { - nGroups = 4; - } - else if (nMTF < 2400) - { - nGroups = 5; - } - else - { - nGroups = 6; - } - - /*--- Generate an initial set of coding tables ---*/ - int nPart = nGroups; - int remF = nMTF; - gs = 0; - while (nPart > 0) - { - int tFreq = remF / nPart; - int aFreq = 0; - ge = gs - 1; - while (aFreq < tFreq && ge < alphaSize - 1) - { - ge++; - aFreq += mtfFreq[ge]; - } - - if (ge > gs && nPart != nGroups && nPart != 1 && ((nGroups - nPart) % 2 == 1)) - { - aFreq -= mtfFreq[ge]; - ge--; - } - - for (int v = 0; v < alphaSize; v++) - { - if (v >= gs && v <= ge) - { - len[nPart - 1][v] = (char)LESSER_ICOST; - } - else - { - len[nPart - 1][v] = (char)GREATER_ICOST; - } - } - - nPart--; - gs = ge + 1; - remF -= aFreq; - } - - int[][] rfreq = new int[BZip2Constants.GroupCount][]; - for (int i = 0; i < BZip2Constants.GroupCount; ++i) - { - rfreq[i] = new int[BZip2Constants.MaximumAlphaSize]; - } - - int[] fave = new int[BZip2Constants.GroupCount]; - short[] cost = new short[BZip2Constants.GroupCount]; - /*--- - Iterate up to N_ITERS times to improve the tables. - ---*/ - for (iter = 0; iter < BZip2Constants.NumberOfIterations; ++iter) - { - for (int t = 0; t < nGroups; ++t) - { - fave[t] = 0; - } - - for (int t = 0; t < nGroups; ++t) - { - for (int v = 0; v < alphaSize; ++v) - { - rfreq[t][v] = 0; - } - } - - nSelectors = 0; - totc = 0; - gs = 0; - while (true) - { - /*--- Set group start & end marks. --*/ - if (gs >= nMTF) - { - break; - } - ge = gs + BZip2Constants.GroupSize - 1; - if (ge >= nMTF) - { - ge = nMTF - 1; - } - - /*-- - Calculate the cost of this group as coded - by each of the coding tables. - --*/ - for (int t = 0; t < nGroups; t++) - { - cost[t] = 0; - } - - if (nGroups == 6) - { - short cost0, cost1, cost2, cost3, cost4, cost5; - cost0 = cost1 = cost2 = cost3 = cost4 = cost5 = 0; - for (int i = gs; i <= ge; ++i) - { - short icv = szptr[i]; - cost0 += (short)len[0][icv]; - cost1 += (short)len[1][icv]; - cost2 += (short)len[2][icv]; - cost3 += (short)len[3][icv]; - cost4 += (short)len[4][icv]; - cost5 += (short)len[5][icv]; - } - cost[0] = cost0; - cost[1] = cost1; - cost[2] = cost2; - cost[3] = cost3; - cost[4] = cost4; - cost[5] = cost5; - } - else - { - for (int i = gs; i <= ge; ++i) - { - short icv = szptr[i]; - for (int t = 0; t < nGroups; t++) - { - cost[t] += (short)len[t][icv]; - } - } - } - - /*-- - Find the coding table which is best for this group, - and record its identity in the selector table. - --*/ - bc = 999999999; - bt = -1; - for (int t = 0; t < nGroups; ++t) - { - if (cost[t] < bc) - { - bc = cost[t]; - bt = t; - } - } - totc += bc; - fave[bt]++; - selector[nSelectors] = (char)bt; - nSelectors++; - - /*-- - Increment the symbol frequencies for the selected table. - --*/ - for (int i = gs; i <= ge; ++i) - { - ++rfreq[bt][szptr[i]]; - } - - gs = ge + 1; - } - - /*-- - Recompute the tables based on the accumulated frequencies. - --*/ - for (int t = 0; t < nGroups; ++t) - { - HbMakeCodeLengths(len[t], rfreq[t], alphaSize, 20); - } - } - - rfreq = null; - fave = null; - cost = null; - - if (!(nGroups < 8)) - { - Panic(); - } - - if (!(nSelectors < 32768 && nSelectors <= (2 + (900000 / BZip2Constants.GroupSize)))) - { - Panic(); - } - - /*--- Compute MTF values for the selectors. ---*/ - char[] pos = new char[BZip2Constants.GroupCount]; - char ll_i, tmp2, tmp; - - for (int i = 0; i < nGroups; i++) - { - pos[i] = (char)i; - } - - for (int i = 0; i < nSelectors; i++) - { - ll_i = selector[i]; - int j = 0; - tmp = pos[j]; - while (ll_i != tmp) - { - j++; - tmp2 = tmp; - tmp = pos[j]; - pos[j] = tmp2; - } - pos[0] = tmp; - selectorMtf[i] = (char)j; - } - - int[][] code = new int[BZip2Constants.GroupCount][]; - - for (int i = 0; i < BZip2Constants.GroupCount; ++i) - { - code[i] = new int[BZip2Constants.MaximumAlphaSize]; - } - - /*--- Assign actual codes for the tables. --*/ - for (int t = 0; t < nGroups; t++) - { - minLen = 32; - maxLen = 0; - for (int i = 0; i < alphaSize; i++) - { - if (len[t][i] > maxLen) - { - maxLen = len[t][i]; - } - if (len[t][i] < minLen) - { - minLen = len[t][i]; - } - } - if (maxLen > 20) - { - Panic(); - } - if (minLen < 1) - { - Panic(); - } - HbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize); - } - - /*--- Transmit the mapping table. ---*/ - bool[] inUse16 = new bool[16]; - for (int i = 0; i < 16; ++i) - { - inUse16[i] = false; - for (int j = 0; j < 16; ++j) - { - if (inUse[i * 16 + j]) - { - inUse16[i] = true; - } - } - } - - for (int i = 0; i < 16; ++i) - { - if (inUse16[i]) - { - BsW(1, 1); - } - else - { - BsW(1, 0); - } - } - - for (int i = 0; i < 16; ++i) - { - if (inUse16[i]) - { - for (int j = 0; j < 16; ++j) - { - if (inUse[i * 16 + j]) - { - BsW(1, 1); - } - else - { - BsW(1, 0); - } - } - } - } - - /*--- Now the selectors. ---*/ - BsW(3, nGroups); - BsW(15, nSelectors); - for (int i = 0; i < nSelectors; ++i) - { - for (int j = 0; j < selectorMtf[i]; ++j) - { - BsW(1, 1); - } - BsW(1, 0); - } - - /*--- Now the coding tables. ---*/ - for (int t = 0; t < nGroups; ++t) - { - int curr = len[t][0]; - BsW(5, curr); - for (int i = 0; i < alphaSize; ++i) - { - while (curr < len[t][i]) - { - BsW(2, 2); - curr++; /* 10 */ - } - while (curr > len[t][i]) - { - BsW(2, 3); - curr--; /* 11 */ - } - BsW(1, 0); - } - } - - /*--- And finally, the block data proper ---*/ - selCtr = 0; - gs = 0; - while (true) - { - if (gs >= nMTF) - { - break; - } - ge = gs + BZip2Constants.GroupSize - 1; - if (ge >= nMTF) - { - ge = nMTF - 1; - } - - for (int i = gs; i <= ge; i++) - { - BsW(len[selector[selCtr]][szptr[i]], code[selector[selCtr]][szptr[i]]); - } - - gs = ge + 1; - ++selCtr; - } - if (!(selCtr == nSelectors)) - { - Panic(); - } - } - - private void MoveToFrontCodeAndSend() - { - BsPutIntVS(24, origPtr); - GenerateMTFValues(); - SendMTFValues(); - } - - private void SimpleSort(int lo, int hi, int d) - { - int i, j, h, bigN, hp; - int v; - - bigN = hi - lo + 1; - if (bigN < 2) - { - return; - } - - hp = 0; - while (increments[hp] < bigN) - { - hp++; - } - hp--; - - for (; hp >= 0; hp--) - { - h = increments[hp]; - - i = lo + h; - while (true) - { - /*-- copy 1 --*/ - if (i > hi) - break; - v = zptr[i]; - j = i; - while (FullGtU(zptr[j - h] + d, v + d)) - { - zptr[j] = zptr[j - h]; - j = j - h; - if (j <= (lo + h - 1)) - break; - } - zptr[j] = v; - i++; - - /*-- copy 2 --*/ - if (i > hi) - { - break; - } - v = zptr[i]; - j = i; - while (FullGtU(zptr[j - h] + d, v + d)) - { - zptr[j] = zptr[j - h]; - j = j - h; - if (j <= (lo + h - 1)) - { - break; - } - } - zptr[j] = v; - i++; - - /*-- copy 3 --*/ - if (i > hi) - { - break; - } - v = zptr[i]; - j = i; - while (FullGtU(zptr[j - h] + d, v + d)) - { - zptr[j] = zptr[j - h]; - j = j - h; - if (j <= (lo + h - 1)) - { - break; - } - } - zptr[j] = v; - i++; - - if (workDone > workLimit && firstAttempt) - { - return; - } - } - } - } - - private void Vswap(int p1, int p2, int n) - { - int temp = 0; - while (n > 0) - { - temp = zptr[p1]; - zptr[p1] = zptr[p2]; - zptr[p2] = temp; - p1++; - p2++; - n--; - } - } - - private void QSort3(int loSt, int hiSt, int dSt) - { - int unLo, unHi, ltLo, gtHi, med, n, m; - int lo, hi, d; - - StackElement[] stack = new StackElement[QSORT_STACK_SIZE]; - - int sp = 0; - - stack[sp].ll = loSt; - stack[sp].hh = hiSt; - stack[sp].dd = dSt; - sp++; - - while (sp > 0) - { - if (sp >= QSORT_STACK_SIZE) - { - Panic(); - } - - sp--; - lo = stack[sp].ll; - hi = stack[sp].hh; - d = stack[sp].dd; - - if (hi - lo < SMALL_THRESH || d > DEPTH_THRESH) - { - SimpleSort(lo, hi, d); - if (workDone > workLimit && firstAttempt) - { - return; - } - continue; - } - - med = Med3(block[zptr[lo] + d + 1], - block[zptr[hi] + d + 1], - block[zptr[(lo + hi) >> 1] + d + 1]); - - unLo = ltLo = lo; - unHi = gtHi = hi; - - while (true) - { - while (true) - { - if (unLo > unHi) - { - break; - } - n = ((int)block[zptr[unLo] + d + 1]) - med; - if (n == 0) - { - int temp = zptr[unLo]; - zptr[unLo] = zptr[ltLo]; - zptr[ltLo] = temp; - ltLo++; - unLo++; - continue; - } - if (n > 0) - { - break; - } - unLo++; - } - - while (true) - { - if (unLo > unHi) - { - break; - } - n = ((int)block[zptr[unHi] + d + 1]) - med; - if (n == 0) - { - int temp = zptr[unHi]; - zptr[unHi] = zptr[gtHi]; - zptr[gtHi] = temp; - gtHi--; - unHi--; - continue; - } - if (n < 0) - { - break; - } - unHi--; - } - - if (unLo > unHi) - { - break; - } - - { - int temp = zptr[unLo]; - zptr[unLo] = zptr[unHi]; - zptr[unHi] = temp; - unLo++; - unHi--; - } - } - - if (gtHi < ltLo) - { - stack[sp].ll = lo; - stack[sp].hh = hi; - stack[sp].dd = d + 1; - sp++; - continue; - } - - n = ((ltLo - lo) < (unLo - ltLo)) ? (ltLo - lo) : (unLo - ltLo); - Vswap(lo, unLo - n, n); - m = ((hi - gtHi) < (gtHi - unHi)) ? (hi - gtHi) : (gtHi - unHi); - Vswap(unLo, hi - m + 1, m); - - n = lo + unLo - ltLo - 1; - m = hi - (gtHi - unHi) + 1; - - stack[sp].ll = lo; - stack[sp].hh = n; - stack[sp].dd = d; - sp++; - - stack[sp].ll = n + 1; - stack[sp].hh = m - 1; - stack[sp].dd = d + 1; - sp++; - - stack[sp].ll = m; - stack[sp].hh = hi; - stack[sp].dd = d; - sp++; - } - } - - private void MainSort() - { - int i, j, ss, sb; - int[] runningOrder = new int[256]; - int[] copy = new int[256]; - bool[] bigDone = new bool[256]; - int c1, c2; - int numQSorted; - - /*-- - In the various block-sized structures, live data runs - from 0 to last+NUM_OVERSHOOT_BYTES inclusive. First, - set up the overshoot area for block. - --*/ - - // if (verbosity >= 4) fprintf ( stderr, " sort initialise ...\n" ); - for (i = 0; i < BZip2Constants.OvershootBytes; i++) - { - block[last + i + 2] = block[(i % (last + 1)) + 1]; - } - for (i = 0; i <= last + BZip2Constants.OvershootBytes; i++) - { - quadrant[i] = 0; - } - - block[0] = (byte)(block[last + 1]); - - if (last < 4000) - { - /*-- - Use simpleSort(), since the full sorting mechanism - has quite a large constant overhead. - --*/ - for (i = 0; i <= last; i++) - { - zptr[i] = i; - } - firstAttempt = false; - workDone = workLimit = 0; - SimpleSort(0, last, 0); - } - else - { - numQSorted = 0; - for (i = 0; i <= 255; i++) - { - bigDone[i] = false; - } - for (i = 0; i <= 65536; i++) - { - ftab[i] = 0; - } - - c1 = block[0]; - for (i = 0; i <= last; i++) - { - c2 = block[i + 1]; - ftab[(c1 << 8) + c2]++; - c1 = c2; - } - - for (i = 1; i <= 65536; i++) - { - ftab[i] += ftab[i - 1]; - } - - c1 = block[1]; - for (i = 0; i < last; i++) - { - c2 = block[i + 2]; - j = (c1 << 8) + c2; - c1 = c2; - ftab[j]--; - zptr[ftab[j]] = i; - } - - j = ((block[last + 1]) << 8) + (block[1]); - ftab[j]--; - zptr[ftab[j]] = last; - - /*-- - Now ftab contains the first loc of every small bucket. - Calculate the running order, from smallest to largest - big bucket. - --*/ - - for (i = 0; i <= 255; i++) - { - runningOrder[i] = i; - } - - int vv; - int h = 1; - do - { - h = 3 * h + 1; - } while (h <= 256); - do - { - h = h / 3; - for (i = h; i <= 255; i++) - { - vv = runningOrder[i]; - j = i; - while ((ftab[((runningOrder[j - h]) + 1) << 8] - ftab[(runningOrder[j - h]) << 8]) > (ftab[((vv) + 1) << 8] - ftab[(vv) << 8])) - { - runningOrder[j] = runningOrder[j - h]; - j = j - h; - if (j <= (h - 1)) - { - break; - } - } - runningOrder[j] = vv; - } - } while (h != 1); - - /*-- - The main sorting loop. - --*/ - for (i = 0; i <= 255; i++) - { - /*-- - Process big buckets, starting with the least full. - --*/ - ss = runningOrder[i]; - - /*-- - Complete the big bucket [ss] by quicksorting - any unsorted small buckets [ss, j]. Hopefully - previous pointer-scanning phases have already - completed many of the small buckets [ss, j], so - we don't have to sort them at all. - --*/ - for (j = 0; j <= 255; j++) - { - sb = (ss << 8) + j; - if (!((ftab[sb] & SETMASK) == SETMASK)) - { - int lo = ftab[sb] & CLEARMASK; - int hi = (ftab[sb + 1] & CLEARMASK) - 1; - if (hi > lo) - { - QSort3(lo, hi, 2); - numQSorted += (hi - lo + 1); - if (workDone > workLimit && firstAttempt) - { - return; - } - } - ftab[sb] |= SETMASK; - } - } - - /*-- - The ss big bucket is now done. Record this fact, - and update the quadrant descriptors. Remember to - update quadrants in the overshoot area too, if - necessary. The "if (i < 255)" test merely skips - this updating for the last bucket processed, since - updating for the last bucket is pointless. - --*/ - bigDone[ss] = true; - - if (i < 255) - { - int bbStart = ftab[ss << 8] & CLEARMASK; - int bbSize = (ftab[(ss + 1) << 8] & CLEARMASK) - bbStart; - int shifts = 0; - - while ((bbSize >> shifts) > 65534) - { - shifts++; - } - - for (j = 0; j < bbSize; j++) - { - int a2update = zptr[bbStart + j]; - int qVal = (j >> shifts); - quadrant[a2update] = qVal; - if (a2update < BZip2Constants.OvershootBytes) - { - quadrant[a2update + last + 1] = qVal; - } - } - - if (!(((bbSize - 1) >> shifts) <= 65535)) - { - Panic(); - } - } - - /*-- - Now scan this big bucket so as to synthesise the - sorted order for small buckets [t, ss] for all t != ss. - --*/ - for (j = 0; j <= 255; j++) - { - copy[j] = ftab[(j << 8) + ss] & CLEARMASK; - } - - for (j = ftab[ss << 8] & CLEARMASK; j < (ftab[(ss + 1) << 8] & CLEARMASK); j++) - { - c1 = block[zptr[j]]; - if (!bigDone[c1]) - { - zptr[copy[c1]] = zptr[j] == 0 ? last : zptr[j] - 1; - copy[c1]++; - } - } - - for (j = 0; j <= 255; j++) - { - ftab[(j << 8) + ss] |= SETMASK; - } - } - } - } - - private void RandomiseBlock() - { - int i; - int rNToGo = 0; - int rTPos = 0; - for (i = 0; i < 256; i++) - { - inUse[i] = false; - } - - for (i = 0; i <= last; i++) - { - if (rNToGo == 0) - { - rNToGo = (int)BZip2Constants.RandomNumbers[rTPos]; - rTPos++; - if (rTPos == 512) - { - rTPos = 0; - } - } - rNToGo--; - block[i + 1] ^= (byte)((rNToGo == 1) ? 1 : 0); - // handle 16 bit signed numbers - block[i + 1] &= 0xFF; - - inUse[block[i + 1]] = true; - } - } - - private void DoReversibleTransformation() - { - workLimit = workFactor * last; - workDone = 0; - blockRandomised = false; - firstAttempt = true; - - MainSort(); - - if (workDone > workLimit && firstAttempt) - { - RandomiseBlock(); - workLimit = workDone = 0; - blockRandomised = true; - firstAttempt = false; - MainSort(); - } - - origPtr = -1; - for (int i = 0; i <= last; i++) - { - if (zptr[i] == 0) - { - origPtr = i; - break; - } - } - - if (origPtr == -1) - { - Panic(); - } - } - - private bool FullGtU(int i1, int i2) - { - int k; - byte c1, c2; - int s1, s2; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return c1 > c2; - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return c1 > c2; - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return c1 > c2; - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return c1 > c2; - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return c1 > c2; - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return c1 > c2; - } - i1++; - i2++; - - k = last + 1; - - do - { - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return c1 > c2; - } - s1 = quadrant[i1]; - s2 = quadrant[i2]; - if (s1 != s2) - { - return s1 > s2; - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return c1 > c2; - } - s1 = quadrant[i1]; - s2 = quadrant[i2]; - if (s1 != s2) - { - return s1 > s2; - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return c1 > c2; - } - s1 = quadrant[i1]; - s2 = quadrant[i2]; - if (s1 != s2) - { - return s1 > s2; - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return c1 > c2; - } - s1 = quadrant[i1]; - s2 = quadrant[i2]; - if (s1 != s2) - { - return s1 > s2; - } - i1++; - i2++; - - if (i1 > last) - { - i1 -= last; - i1--; - } - if (i2 > last) - { - i2 -= last; - i2--; - } - - k -= 4; - ++workDone; - } while (k >= 0); - - return false; - } - - private void AllocateCompressStructures() - { - int n = BZip2Constants.BaseBlockSize * blockSize100k; - block = new byte[(n + 1 + BZip2Constants.OvershootBytes)]; - quadrant = new int[(n + BZip2Constants.OvershootBytes)]; - zptr = new int[n]; - ftab = new int[65537]; - - if (block == null || quadrant == null || zptr == null || ftab == null) - { - // int totalDraw = (n + 1 + NUM_OVERSHOOT_BYTES) + (n + NUM_OVERSHOOT_BYTES) + n + 65537; - // compressOutOfMemory ( totalDraw, n ); - } - - /* - The back end needs a place to store the MTF values - whilst it calculates the coding tables. We could - put them in the zptr array. However, these values - will fit in a short, so we overlay szptr at the - start of zptr, in the hope of reducing the number - of cache misses induced by the multiple traversals - of the MTF values when calculating coding tables. - Seems to improve compression speed by about 1%. - */ - // szptr = zptr; - - szptr = new short[2 * n]; - } - - private void GenerateMTFValues() - { - char[] yy = new char[256]; - int i, j; - char tmp; - char tmp2; - int zPend; - int wr; - int EOB; - - MakeMaps(); - EOB = nInUse + 1; - - for (i = 0; i <= EOB; i++) - { - mtfFreq[i] = 0; - } - - wr = 0; - zPend = 0; - for (i = 0; i < nInUse; i++) - { - yy[i] = (char)i; - } - - for (i = 0; i <= last; i++) - { - char ll_i; - - ll_i = unseqToSeq[block[zptr[i]]]; - - j = 0; - tmp = yy[j]; - while (ll_i != tmp) - { - j++; - tmp2 = tmp; - tmp = yy[j]; - yy[j] = tmp2; - } - yy[0] = tmp; - - if (j == 0) - { - zPend++; - } - else - { - if (zPend > 0) - { - zPend--; - while (true) - { - switch (zPend % 2) - { - case 0: - szptr[wr] = (short)BZip2Constants.RunA; - wr++; - mtfFreq[BZip2Constants.RunA]++; - break; - - case 1: - szptr[wr] = (short)BZip2Constants.RunB; - wr++; - mtfFreq[BZip2Constants.RunB]++; - break; - } - if (zPend < 2) - { - break; - } - zPend = (zPend - 2) / 2; - } - zPend = 0; - } - szptr[wr] = (short)(j + 1); - wr++; - mtfFreq[j + 1]++; - } - } - - if (zPend > 0) - { - zPend--; - while (true) - { - switch (zPend % 2) - { - case 0: - szptr[wr] = (short)BZip2Constants.RunA; - wr++; - mtfFreq[BZip2Constants.RunA]++; - break; - - case 1: - szptr[wr] = (short)BZip2Constants.RunB; - wr++; - mtfFreq[BZip2Constants.RunB]++; - break; - } - if (zPend < 2) - { - break; - } - zPend = (zPend - 2) / 2; - } - } - - szptr[wr] = (short)EOB; - wr++; - mtfFreq[EOB]++; - - nMTF = wr; - } - - private static void Panic() - { - throw new BZip2Exception("BZip2 output stream panic"); - } - - private static void HbMakeCodeLengths(char[] len, int[] freq, int alphaSize, int maxLen) - { - /*-- - Nodes and heap entries run from 1. Entry 0 - for both the heap and nodes is a sentinel. - --*/ - int nNodes, nHeap, n1, n2, j, k; - bool tooLong; - - int[] heap = new int[BZip2Constants.MaximumAlphaSize + 2]; - int[] weight = new int[BZip2Constants.MaximumAlphaSize * 2]; - int[] parent = new int[BZip2Constants.MaximumAlphaSize * 2]; - - for (int i = 0; i < alphaSize; ++i) - { - weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8; - } - - while (true) - { - nNodes = alphaSize; - nHeap = 0; - - heap[0] = 0; - weight[0] = 0; - parent[0] = -2; - - for (int i = 1; i <= alphaSize; ++i) - { - parent[i] = -1; - nHeap++; - heap[nHeap] = i; - int zz = nHeap; - int tmp = heap[zz]; - while (weight[tmp] < weight[heap[zz >> 1]]) - { - heap[zz] = heap[zz >> 1]; - zz >>= 1; - } - heap[zz] = tmp; - } - if (!(nHeap < (BZip2Constants.MaximumAlphaSize + 2))) - { - Panic(); - } - - while (nHeap > 1) - { - n1 = heap[1]; - heap[1] = heap[nHeap]; - nHeap--; - int zz = 1; - int yy = 0; - int tmp = heap[zz]; - while (true) - { - yy = zz << 1; - if (yy > nHeap) - { - break; - } - if (yy < nHeap && weight[heap[yy + 1]] < weight[heap[yy]]) - { - yy++; - } - if (weight[tmp] < weight[heap[yy]]) - { - break; - } - - heap[zz] = heap[yy]; - zz = yy; - } - heap[zz] = tmp; - n2 = heap[1]; - heap[1] = heap[nHeap]; - nHeap--; - - zz = 1; - yy = 0; - tmp = heap[zz]; - while (true) - { - yy = zz << 1; - if (yy > nHeap) - { - break; - } - if (yy < nHeap && weight[heap[yy + 1]] < weight[heap[yy]]) - { - yy++; - } - if (weight[tmp] < weight[heap[yy]]) - { - break; - } - heap[zz] = heap[yy]; - zz = yy; - } - heap[zz] = tmp; - nNodes++; - parent[n1] = parent[n2] = nNodes; - - weight[nNodes] = (int)((weight[n1] & 0xffffff00) + (weight[n2] & 0xffffff00)) | - (int)(1 + (((weight[n1] & 0x000000ff) > (weight[n2] & 0x000000ff)) ? (weight[n1] & 0x000000ff) : (weight[n2] & 0x000000ff))); - - parent[nNodes] = -1; - nHeap++; - heap[nHeap] = nNodes; - - zz = nHeap; - tmp = heap[zz]; - while (weight[tmp] < weight[heap[zz >> 1]]) - { - heap[zz] = heap[zz >> 1]; - zz >>= 1; - } - heap[zz] = tmp; - } - if (!(nNodes < (BZip2Constants.MaximumAlphaSize * 2))) - { - Panic(); - } - - tooLong = false; - for (int i = 1; i <= alphaSize; ++i) - { - j = 0; - k = i; - while (parent[k] >= 0) - { - k = parent[k]; - j++; - } - len[i - 1] = (char)j; - tooLong |= j > maxLen; - } - - if (!tooLong) - { - break; - } - - for (int i = 1; i < alphaSize; ++i) - { - j = weight[i] >> 8; - j = 1 + (j / 2); - weight[i] = j << 8; - } - } - } - - private static void HbAssignCodes(int[] code, char[] length, int minLen, int maxLen, int alphaSize) - { - int vec = 0; - for (int n = minLen; n <= maxLen; ++n) - { - for (int i = 0; i < alphaSize; ++i) - { - if (length[i] == n) - { - code[i] = vec; - ++vec; - } - } - vec <<= 1; - } - } - - private static byte Med3(byte a, byte b, byte c) - { - byte t; - if (a > b) - { - t = a; - a = b; - b = t; - } - if (b > c) - { - t = b; - b = c; - c = t; - } - if (a > b) - { - b = a; - } - return b; - } - - private struct StackElement - { - public int ll; - public int hh; - public int dd; - } - } -} diff --git a/ICSharpCode.SharpZipLib/Checksum/Adler32.cs b/ICSharpCode.SharpZipLib/Checksum/Adler32.cs deleted file mode 100644 index b2a0f151a12c..000000000000 --- a/ICSharpCode.SharpZipLib/Checksum/Adler32.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System; - -namespace ICSharpCode.SharpZipLib.Checksum -{ - /// - /// Computes Adler32 checksum for a stream of data. An Adler32 - /// checksum is not as reliable as a CRC32 checksum, but a lot faster to - /// compute. - /// - /// The specification for Adler32 may be found in RFC 1950. - /// ZLIB Compressed Data Format Specification version 3.3) - /// - /// - /// From that document: - /// - /// "ADLER32 (Adler-32 checksum) - /// This contains a checksum value of the uncompressed data - /// (excluding any dictionary data) computed according to Adler-32 - /// algorithm. This algorithm is a 32-bit extension and improvement - /// of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073 - /// standard. - /// - /// Adler-32 is composed of two sums accumulated per byte: s1 is - /// the sum of all bytes, s2 is the sum of all s1 values. Both sums - /// are done modulo 65521. s1 is initialized to 1, s2 to zero. The - /// Adler-32 checksum is stored as s2*65536 + s1 in most- - /// significant-byte first (network) order." - /// - /// "8.2. The Adler-32 algorithm - /// - /// The Adler-32 algorithm is much faster than the CRC32 algorithm yet - /// still provides an extremely low probability of undetected errors. - /// - /// The modulo on unsigned long accumulators can be delayed for 5552 - /// bytes, so the modulo operation time is negligible. If the bytes - /// are a, b, c, the second sum is 3a + 2b + c + 3, and so is position - /// and order sensitive, unlike the first sum, which is just a - /// checksum. That 65521 is prime is important to avoid a possible - /// large class of two-byte errors that leave the check unchanged. - /// (The Fletcher checksum uses 255, which is not prime and which also - /// makes the Fletcher check insensitive to single byte changes 0 - - /// 255.) - /// - /// The sum s1 is initialized to 1 instead of zero to make the length - /// of the sequence part of s2, so that the length does not have to be - /// checked separately. (Any sequence of zeroes has a Fletcher - /// checksum of zero.)" - /// - /// - /// - public sealed class Adler32 : IChecksum - { - #region Instance Fields - - /// - /// largest prime smaller than 65536 - /// - private static readonly uint BASE = 65521; - - /// - /// The CRC data checksum so far. - /// - private uint checkValue; - - #endregion Instance Fields - - /// - /// Initialise a default instance of - /// - public Adler32() - { - Reset(); - } - - /// - /// Resets the Adler32 data checksum as if no update was ever called. - /// - public void Reset() - { - checkValue = 1; - } - - /// - /// Returns the Adler32 data checksum computed so far. - /// - public long Value - { - get - { - return checkValue; - } - } - - /// - /// Updates the checksum with the byte b. - /// - /// - /// The data value to add. The high byte of the int is ignored. - /// - public void Update(int bval) - { - // We could make a length 1 byte array and call update again, but I - // would rather not have that overhead - uint s1 = checkValue & 0xFFFF; - uint s2 = checkValue >> 16; - - s1 = (s1 + ((uint)bval & 0xFF)) % BASE; - s2 = (s1 + s2) % BASE; - - checkValue = (s2 << 16) + s1; - } - - /// - /// Updates the Adler32 data checksum with the bytes taken from - /// a block of data. - /// - /// Contains the data to update the checksum with. - public void Update(byte[] buffer) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - Update(new ArraySegment(buffer, 0, buffer.Length)); - } - - /// - /// Update Adler32 data checksum based on a portion of a block of data - /// - /// - /// The chunk of data to add - /// - public void Update(ArraySegment segment) - { - //(By Per Bothner) - uint s1 = checkValue & 0xFFFF; - uint s2 = checkValue >> 16; - var count = segment.Count; - var offset = segment.Offset; - while (count > 0) - { - // We can defer the modulo operation: - // s1 maximally grows from 65521 to 65521 + 255 * 3800 - // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31 - int n = 3800; - if (n > count) - { - n = count; - } - count -= n; - while (--n >= 0) - { - s1 = s1 + (uint)(segment.Array[offset++] & 0xff); - s2 = s2 + s1; - } - s1 %= BASE; - s2 %= BASE; - } - checkValue = (s2 << 16) | s1; - } - } -} diff --git a/ICSharpCode.SharpZipLib/Checksum/BZip2Crc.cs b/ICSharpCode.SharpZipLib/Checksum/BZip2Crc.cs deleted file mode 100644 index be76da11eb16..000000000000 --- a/ICSharpCode.SharpZipLib/Checksum/BZip2Crc.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Runtime.CompilerServices; - -namespace ICSharpCode.SharpZipLib.Checksum -{ - /// - /// CRC-32 with unreversed data and reversed output - /// - /// - /// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: - /// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0. - /// - /// Polynomials over GF(2) are represented in binary, one bit per coefficient, - /// with the lowest powers in the most significant bit. Then adding polynomials - /// is just exclusive-or, and multiplying a polynomial by x is a right shift by - /// one. If we call the above polynomial p, and represent a byte as the - /// polynomial q, also with the lowest power in the most significant bit (so the - /// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, - /// where a mod b means the remainder after dividing a by b. - /// - /// This calculation is done using the shift-register method of multiplying and - /// taking the remainder. The register is initialized to zero, and for each - /// incoming bit, x^32 is added mod p to the register if the bit is a one (where - /// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by - /// x (which is shifting right by one and adding x^32 mod p if the bit shifted - /// out is a one). We start with the highest power (least significant bit) of - /// q and repeat for all eight bits of q. - /// - /// This implementation uses sixteen lookup tables stored in one linear array - /// to implement the slicing-by-16 algorithm, a variant of the slicing-by-8 - /// algorithm described in this Intel white paper: - /// - /// https://web.archive.org/web/20120722193753/http://download.intel.com/technology/comms/perfnet/download/slicing-by-8.pdf - /// - /// The first lookup table is simply the CRC of all possible eight bit values. - /// Each successive lookup table is derived from the original table generated - /// by Sarwate's algorithm. Slicing a 16-bit input and XORing the outputs - /// together will produce the same output as a byte-by-byte CRC loop with - /// fewer arithmetic and bit manipulation operations, at the cost of increased - /// memory consumed by the lookup tables. (Slicing-by-16 requires a 16KB table, - /// which is still small enough to fit in most processors' L1 cache.) - /// - public sealed class BZip2Crc : IChecksum - { - #region Instance Fields - - private const uint crcInit = 0xFFFFFFFF; - //const uint crcXor = 0x00000000; - - private static readonly uint[] crcTable = CrcUtilities.GenerateSlicingLookupTable(0x04C11DB7, isReversed: false); - - /// - /// The CRC data checksum so far. - /// - private uint checkValue; - - #endregion Instance Fields - - /// - /// Initialise a default instance of - /// - public BZip2Crc() - { - Reset(); - } - - /// - /// Resets the CRC data checksum as if no update was ever called. - /// - public void Reset() - { - checkValue = crcInit; - } - - /// - /// Returns the CRC data checksum computed so far. - /// - /// Reversed Out = true - public long Value - { - get - { - // Technically, the output should be: - //return (long)(~checkValue ^ crcXor); - // but x ^ 0 = x, so there is no point in adding - // the XOR operation - return (long)(~checkValue); - } - } - - /// - /// Updates the checksum with the int bval. - /// - /// - /// the byte is taken as the lower 8 bits of bval - /// - /// Reversed Data = false - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Update(int bval) - { - checkValue = unchecked(crcTable[(byte)(((checkValue >> 24) & 0xFF) ^ bval)] ^ (checkValue << 8)); - } - - /// - /// Updates the CRC data checksum with the bytes taken from - /// a block of data. - /// - /// Contains the data to update the CRC with. - public void Update(byte[] buffer) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - Update(buffer, 0, buffer.Length); - } - - /// - /// Update CRC data checksum based on a portion of a block of data - /// - /// - /// The chunk of data to add - /// - public void Update(ArraySegment segment) - { - Update(segment.Array, segment.Offset, segment.Count); - } - - /// - /// Internal helper function for updating a block of data using slicing. - /// - /// The array containing the data to add - /// Range start for (inclusive) - /// The number of bytes to checksum starting from - private void Update(byte[] data, int offset, int count) - { - int remainder = count % CrcUtilities.SlicingDegree; - int end = offset + count - remainder; - - while (offset != end) - { - checkValue = CrcUtilities.UpdateDataForNormalPoly(data, offset, crcTable, checkValue); - offset += CrcUtilities.SlicingDegree; - } - - if (remainder != 0) - { - SlowUpdateLoop(data, offset, end + remainder); - } - } - - /// - /// A non-inlined function for updating data that doesn't fit in a 16-byte - /// block. We don't expect to enter this function most of the time, and when - /// we do we're not here for long, so disabling inlining here improves - /// performance overall. - /// - /// The array containing the data to add - /// Range start for (inclusive) - /// Range end for (exclusive) - [MethodImpl(MethodImplOptions.NoInlining)] - private void SlowUpdateLoop(byte[] data, int offset, int end) - { - while (offset != end) - { - Update(data[offset++]); - } - } - } -} diff --git a/ICSharpCode.SharpZipLib/Checksum/Crc32.cs b/ICSharpCode.SharpZipLib/Checksum/Crc32.cs deleted file mode 100644 index 740aff566835..000000000000 --- a/ICSharpCode.SharpZipLib/Checksum/Crc32.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Runtime.CompilerServices; - -namespace ICSharpCode.SharpZipLib.Checksum -{ - /// - /// CRC-32 with reversed data and unreversed output - /// - /// - /// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: - /// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0. - /// - /// Polynomials over GF(2) are represented in binary, one bit per coefficient, - /// with the lowest powers in the most significant bit. Then adding polynomials - /// is just exclusive-or, and multiplying a polynomial by x is a right shift by - /// one. If we call the above polynomial p, and represent a byte as the - /// polynomial q, also with the lowest power in the most significant bit (so the - /// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, - /// where a mod b means the remainder after dividing a by b. - /// - /// This calculation is done using the shift-register method of multiplying and - /// taking the remainder. The register is initialized to zero, and for each - /// incoming bit, x^32 is added mod p to the register if the bit is a one (where - /// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by - /// x (which is shifting right by one and adding x^32 mod p if the bit shifted - /// out is a one). We start with the highest power (least significant bit) of - /// q and repeat for all eight bits of q. - /// - /// This implementation uses sixteen lookup tables stored in one linear array - /// to implement the slicing-by-16 algorithm, a variant of the slicing-by-8 - /// algorithm described in this Intel white paper: - /// - /// https://web.archive.org/web/20120722193753/http://download.intel.com/technology/comms/perfnet/download/slicing-by-8.pdf - /// - /// The first lookup table is simply the CRC of all possible eight bit values. - /// Each successive lookup table is derived from the original table generated - /// by Sarwate's algorithm. Slicing a 16-bit input and XORing the outputs - /// together will produce the same output as a byte-by-byte CRC loop with - /// fewer arithmetic and bit manipulation operations, at the cost of increased - /// memory consumed by the lookup tables. (Slicing-by-16 requires a 16KB table, - /// which is still small enough to fit in most processors' L1 cache.) - /// - public sealed class Crc32 : IChecksum - { - #region Instance Fields - - private static readonly uint crcInit = 0xFFFFFFFF; - private static readonly uint crcXor = 0xFFFFFFFF; - - private static readonly uint[] crcTable = CrcUtilities.GenerateSlicingLookupTable(0xEDB88320, isReversed: true); - - /// - /// The CRC data checksum so far. - /// - private uint checkValue; - - #endregion Instance Fields - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static uint ComputeCrc32(uint oldCrc, byte bval) - { - return (uint)(Crc32.crcTable[(oldCrc ^ bval) & 0xFF] ^ (oldCrc >> 8)); - } - - /// - /// Initialise a default instance of - /// - public Crc32() - { - Reset(); - } - - /// - /// Resets the CRC data checksum as if no update was ever called. - /// - public void Reset() - { - checkValue = crcInit; - } - - /// - /// Returns the CRC data checksum computed so far. - /// - /// Reversed Out = false - public long Value - { - get - { - return (long)(checkValue ^ crcXor); - } - } - - /// - /// Updates the checksum with the int bval. - /// - /// - /// the byte is taken as the lower 8 bits of bval - /// - /// Reversed Data = true - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Update(int bval) - { - checkValue = unchecked(crcTable[(checkValue ^ bval) & 0xFF] ^ (checkValue >> 8)); - } - - /// - /// Updates the CRC data checksum with the bytes taken from - /// a block of data. - /// - /// Contains the data to update the CRC with. - public void Update(byte[] buffer) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - Update(buffer, 0, buffer.Length); - } - - /// - /// Update CRC data checksum based on a portion of a block of data - /// - /// - /// The chunk of data to add - /// - public void Update(ArraySegment segment) - { - Update(segment.Array, segment.Offset, segment.Count); - } - - /// - /// Internal helper function for updating a block of data using slicing. - /// - /// The array containing the data to add - /// Range start for (inclusive) - /// The number of bytes to checksum starting from - private void Update(byte[] data, int offset, int count) - { - int remainder = count % CrcUtilities.SlicingDegree; - int end = offset + count - remainder; - - while (offset != end) - { - checkValue = CrcUtilities.UpdateDataForReversedPoly(data, offset, crcTable, checkValue); - offset += CrcUtilities.SlicingDegree; - } - - if (remainder != 0) - { - SlowUpdateLoop(data, offset, end + remainder); - } - } - - /// - /// A non-inlined function for updating data that doesn't fit in a 16-byte - /// block. We don't expect to enter this function most of the time, and when - /// we do we're not here for long, so disabling inlining here improves - /// performance overall. - /// - /// The array containing the data to add - /// Range start for (inclusive) - /// Range end for (exclusive) - [MethodImpl(MethodImplOptions.NoInlining)] - private void SlowUpdateLoop(byte[] data, int offset, int end) - { - while (offset != end) - { - Update(data[offset++]); - } - } - } -} diff --git a/ICSharpCode.SharpZipLib/Checksum/CrcUtilities.cs b/ICSharpCode.SharpZipLib/Checksum/CrcUtilities.cs deleted file mode 100644 index 575abe08b5c3..000000000000 --- a/ICSharpCode.SharpZipLib/Checksum/CrcUtilities.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace ICSharpCode.SharpZipLib.Checksum -{ - internal static class CrcUtilities - { - /// - /// The number of slicing lookup tables to generate. - /// - internal const int SlicingDegree = 16; - - /// - /// Generates multiple CRC lookup tables for a given polynomial, stored - /// in a linear array of uints. The first block (i.e. the first 256 - /// elements) is the same as the byte-by-byte CRC lookup table. - /// - /// The generating CRC polynomial - /// Whether the polynomial is in reversed bit order - /// A linear array of 256 * elements - /// - /// This table could also be generated as a rectangular array, but the - /// JIT compiler generates slower code than if we use a linear array. - /// Known issue, see: https://github.com/dotnet/runtime/issues/30275 - /// - internal static uint[] GenerateSlicingLookupTable(uint polynomial, bool isReversed) - { - var table = new uint[256 * SlicingDegree]; - uint one = isReversed ? 1 : (1U << 31); - - for (int i = 0; i < 256; i++) - { - uint res = (uint)(isReversed ? i : i << 24); - for (int j = 0; j < SlicingDegree; j++) - { - for (int k = 0; k < 8; k++) - { - if (isReversed) - { - res = (res & one) == 1 ? polynomial ^ (res >> 1) : res >> 1; - } - else - { - res = (res & one) != 0 ? polynomial ^ (res << 1) : res << 1; - } - } - - table[(256 * j) + i] = res; - } - } - - return table; - } - - /// - /// Mixes the first four bytes of input with - /// using normal ordering before calling . - /// - /// Array of data to checksum - /// Offset to start reading from - /// The table to use for slicing-by-16 lookup - /// Checksum state before this update call - /// A new unfinalized checksum value - /// - /// - /// Assumes input[offset]..input[offset + 15] are valid array indexes. - /// For performance reasons, this must be checked by the caller. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static uint UpdateDataForNormalPoly(byte[] input, int offset, uint[] crcTable, uint checkValue) - { - byte x1 = (byte)((byte)(checkValue >> 24) ^ input[offset]); - byte x2 = (byte)((byte)(checkValue >> 16) ^ input[offset + 1]); - byte x3 = (byte)((byte)(checkValue >> 8) ^ input[offset + 2]); - byte x4 = (byte)((byte)checkValue ^ input[offset + 3]); - - return UpdateDataCommon(input, offset, crcTable, x1, x2, x3, x4); - } - - /// - /// Mixes the first four bytes of input with - /// using reflected ordering before calling . - /// - /// Array of data to checksum - /// Offset to start reading from - /// The table to use for slicing-by-16 lookup - /// Checksum state before this update call - /// A new unfinalized checksum value - /// - /// - /// Assumes input[offset]..input[offset + 15] are valid array indexes. - /// For performance reasons, this must be checked by the caller. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static uint UpdateDataForReversedPoly(byte[] input, int offset, uint[] crcTable, uint checkValue) - { - byte x1 = (byte)((byte)checkValue ^ input[offset]); - byte x2 = (byte)((byte)(checkValue >>= 8) ^ input[offset + 1]); - byte x3 = (byte)((byte)(checkValue >>= 8) ^ input[offset + 2]); - byte x4 = (byte)((byte)(checkValue >>= 8) ^ input[offset + 3]); - - return UpdateDataCommon(input, offset, crcTable, x1, x2, x3, x4); - } - - /// - /// A shared method for updating an unfinalized CRC checksum using slicing-by-16. - /// - /// Array of data to checksum - /// Offset to start reading from - /// The table to use for slicing-by-16 lookup - /// First byte of input after mixing with the old CRC - /// Second byte of input after mixing with the old CRC - /// Third byte of input after mixing with the old CRC - /// Fourth byte of input after mixing with the old CRC - /// A new unfinalized checksum value - /// - /// - /// Even though the first four bytes of input are fed in as arguments, - /// should be the same value passed to this - /// function's caller (either or - /// ). This method will get inlined - /// into both functions, so using the same offset produces faster code. - /// - /// - /// Because most processors running C# have some kind of instruction-level - /// parallelism, the order of XOR operations can affect performance. This - /// ordering assumes that the assembly code generated by the just-in-time - /// compiler will emit a bunch of arithmetic operations for checking array - /// bounds. Then it opportunistically XORs a1 and a2 to keep the processor - /// busy while those other parts of the pipeline handle the range check - /// calculations. - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint UpdateDataCommon(byte[] input, int offset, uint[] crcTable, byte x1, byte x2, byte x3, byte x4) - { - uint result; - uint a1 = crcTable[x1 + 3840] ^ crcTable[x2 + 3584]; - uint a2 = crcTable[x3 + 3328] ^ crcTable[x4 + 3072]; - - result = crcTable[input[offset + 4] + 2816]; - result ^= crcTable[input[offset + 5] + 2560]; - a1 ^= crcTable[input[offset + 9] + 1536]; - result ^= crcTable[input[offset + 6] + 2304]; - result ^= crcTable[input[offset + 7] + 2048]; - result ^= crcTable[input[offset + 8] + 1792]; - a2 ^= crcTable[input[offset + 13] + 512]; - result ^= crcTable[input[offset + 10] + 1280]; - result ^= crcTable[input[offset + 11] + 1024]; - result ^= crcTable[input[offset + 12] + 768]; - result ^= a1; - result ^= crcTable[input[offset + 14] + 256]; - result ^= crcTable[input[offset + 15]]; - result ^= a2; - - return result; - } - } -} diff --git a/ICSharpCode.SharpZipLib/Checksum/IChecksum.cs b/ICSharpCode.SharpZipLib/Checksum/IChecksum.cs deleted file mode 100644 index db74a5a5d20a..000000000000 --- a/ICSharpCode.SharpZipLib/Checksum/IChecksum.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; - -namespace ICSharpCode.SharpZipLib.Checksum -{ - /// - /// Interface to compute a data checksum used by checked input/output streams. - /// A data checksum can be updated by one byte or with a byte array. After each - /// update the value of the current checksum can be returned by calling - /// getValue. The complete checksum object can also be reset - /// so it can be used again with new data. - /// - public interface IChecksum - { - /// - /// Resets the data checksum as if no update was ever called. - /// - void Reset(); - - /// - /// Returns the data checksum computed so far. - /// - long Value - { - get; - } - - /// - /// Adds one byte to the data checksum. - /// - /// - /// the data value to add. The high byte of the int is ignored. - /// - void Update(int bval); - - /// - /// Updates the data checksum with the bytes taken from the array. - /// - /// - /// buffer an array of bytes - /// - void Update(byte[] buffer); - - /// - /// Adds the byte array to the data checksum. - /// - /// - /// The chunk of data to add - /// - void Update(ArraySegment segment); - } -} diff --git a/ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs b/ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs deleted file mode 100644 index a2e30da7f2ee..000000000000 --- a/ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System.IO; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using CT = System.Threading.CancellationToken; - -// ReSharper disable MemberCanBePrivate.Global -// ReSharper disable InconsistentNaming - -namespace ICSharpCode.SharpZipLib.Core -{ - internal static class ByteOrderStreamExtensions - { - internal static byte[] SwappedBytes(ushort value) => new[] {(byte)value, (byte)(value >> 8)}; - internal static byte[] SwappedBytes(short value) => new[] {(byte)value, (byte)(value >> 8)}; - internal static byte[] SwappedBytes(uint value) => new[] {(byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24)}; - internal static byte[] SwappedBytes(int value) => new[] {(byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24)}; - - internal static byte[] SwappedBytes(long value) => new[] { - (byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24), - (byte)(value >> 32), (byte)(value >> 40), (byte)(value >> 48), (byte)(value >> 56) - }; - - internal static byte[] SwappedBytes(ulong value) => new[] { - (byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24), - (byte)(value >> 32), (byte)(value >> 40), (byte)(value >> 48), (byte)(value >> 56) - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static long SwappedS64(byte[] bytes) => ( - (long)bytes[0] << 0 | (long)bytes[1] << 8 | (long)bytes[2] << 16 | (long)bytes[3] << 24 | - (long)bytes[4] << 32 | (long)bytes[5] << 40 | (long)bytes[6] << 48 | (long)bytes[7] << 56); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ulong SwappedU64(byte[] bytes) => ( - (ulong)bytes[0] << 0 | (ulong)bytes[1] << 8 | (ulong)bytes[2] << 16 | (ulong)bytes[3] << 24 | - (ulong)bytes[4] << 32 | (ulong)bytes[5] << 40 | (ulong)bytes[6] << 48 | (ulong)bytes[7] << 56); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int SwappedS32(byte[] bytes) => bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static uint SwappedU32(byte[] bytes) => (uint) SwappedS32(bytes); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static short SwappedS16(byte[] bytes) => (short)(bytes[0] | bytes[1] << 8); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ushort SwappedU16(byte[] bytes) => (ushort) SwappedS16(bytes); - - internal static byte[] ReadBytes(this Stream stream, int count) - { - var bytes = new byte[count]; - var remaining = count; - while (remaining > 0) - { - var bytesRead = stream.Read(bytes, count - remaining, remaining); - if (bytesRead < 1) throw new EndOfStreamException(); - remaining -= bytesRead; - } - - return bytes; - } - - /// Read an unsigned short in little endian byte order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int ReadLEShort(this Stream stream) => SwappedS16(ReadBytes(stream, 2)); - - /// Read an int in little endian byte order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int ReadLEInt(this Stream stream) => SwappedS32(ReadBytes(stream, 4)); - - /// Read a long in little endian byte order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long ReadLELong(this Stream stream) => SwappedS64(ReadBytes(stream, 8)); - - /// Write an unsigned short in little endian byte order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteLEShort(this Stream stream, int value) => stream.Write(SwappedBytes(value), 0, 2); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static async Task WriteLEShortAsync(this Stream stream, int value, CT ct) - => await stream.WriteAsync(SwappedBytes(value), 0, 2, ct); - - /// Write a ushort in little endian byte order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteLEUshort(this Stream stream, ushort value) => stream.Write(SwappedBytes(value), 0, 2); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static async Task WriteLEUshortAsync(this Stream stream, ushort value, CT ct) - => await stream.WriteAsync(SwappedBytes(value), 0, 2, ct); - - /// Write an int in little endian byte order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteLEInt(this Stream stream, int value) => stream.Write(SwappedBytes(value), 0, 4); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static async Task WriteLEIntAsync(this Stream stream, int value, CT ct) - => await stream.WriteAsync(SwappedBytes(value), 0, 4, ct); - - /// Write a uint in little endian byte order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteLEUint(this Stream stream, uint value) => stream.Write(SwappedBytes(value), 0, 4); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static async Task WriteLEUintAsync(this Stream stream, uint value, CT ct) - => await stream.WriteAsync(SwappedBytes(value), 0, 4, ct); - - /// Write a long in little endian byte order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteLELong(this Stream stream, long value) => stream.Write(SwappedBytes(value), 0, 8); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static async Task WriteLELongAsync(this Stream stream, long value, CT ct) - => await stream.WriteAsync(SwappedBytes(value), 0, 8, ct); - - /// Write a ulong in little endian byte order. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteLEUlong(this Stream stream, ulong value) => stream.Write(SwappedBytes(value), 0, 8); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static async Task WriteLEUlongAsync(this Stream stream, ulong value, CT ct) - => await stream.WriteAsync(SwappedBytes(value), 0, 8, ct); - } -} diff --git a/ICSharpCode.SharpZipLib/Core/EmptyRefs.cs b/ICSharpCode.SharpZipLib/Core/EmptyRefs.cs deleted file mode 100644 index feb7a8e17cb9..000000000000 --- a/ICSharpCode.SharpZipLib/Core/EmptyRefs.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace ICSharpCode.SharpZipLib.Core -{ - internal static class Empty - { -#if NET45 - internal static class EmptyArray - { - public static readonly T[] Value = new T[0]; - } - public static T[] Array() => EmptyArray.Value; -#else - public static T[] Array() => System.Array.Empty(); -#endif - } -} diff --git a/ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs b/ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs deleted file mode 100644 index eb14e2d49f9a..000000000000 --- a/ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib -{ - /// - /// SharpZipBaseException is the base exception class for SharpZipLib. - /// All library exceptions are derived from this. - /// - /// NOTE: Not all exceptions thrown will be derived from this class. - /// A variety of other exceptions are possible for example - [Serializable] - public class SharpZipBaseException : Exception - { - /// - /// Initializes a new instance of the SharpZipBaseException class. - /// - public SharpZipBaseException() - { - } - - /// - /// Initializes a new instance of the SharpZipBaseException class with a specified error message. - /// - /// A message describing the exception. - public SharpZipBaseException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the SharpZipBaseException class with a specified - /// error message and a reference to the inner exception that is the cause of this exception. - /// - /// A message describing the exception. - /// The inner exception - public SharpZipBaseException(string message, Exception innerException) - : base(message, innerException) - { - } - - /// - /// Initializes a new instance of the SharpZipBaseException class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected SharpZipBaseException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs b/ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs deleted file mode 100644 index e22b11b0d164..000000000000 --- a/ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib -{ - /// - /// Indicates that an error occurred during decoding of a input stream due to corrupt - /// data or (unintentional) library incompatibility. - /// - [Serializable] - public class StreamDecodingException : SharpZipBaseException - { - private const string GenericMessage = "Input stream could not be decoded"; - - /// - /// Initializes a new instance of the StreamDecodingException with a generic message - /// - public StreamDecodingException() : base(GenericMessage) { } - - /// - /// Initializes a new instance of the StreamDecodingException class with a specified error message. - /// - /// A message describing the exception. - public StreamDecodingException(string message) : base(message) { } - - /// - /// Initializes a new instance of the StreamDecodingException class with a specified - /// error message and a reference to the inner exception that is the cause of this exception. - /// - /// A message describing the exception. - /// The inner exception - public StreamDecodingException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the StreamDecodingException class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected StreamDecodingException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs b/ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs deleted file mode 100644 index 5827e559d0b1..000000000000 --- a/ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib -{ - /// - /// Indicates that the input stream could not decoded due to known library incompability or missing features - /// - [Serializable] - public class StreamUnsupportedException : StreamDecodingException - { - private const string GenericMessage = "Input stream is in a unsupported format"; - - /// - /// Initializes a new instance of the StreamUnsupportedException with a generic message - /// - public StreamUnsupportedException() : base(GenericMessage) { } - - /// - /// Initializes a new instance of the StreamUnsupportedException class with a specified error message. - /// - /// A message describing the exception. - public StreamUnsupportedException(string message) : base(message) { } - - /// - /// Initializes a new instance of the StreamUnsupportedException class with a specified - /// error message and a reference to the inner exception that is the cause of this exception. - /// - /// A message describing the exception. - /// The inner exception - public StreamUnsupportedException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the StreamUnsupportedException class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected StreamUnsupportedException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs b/ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs deleted file mode 100644 index a35c49f0379e..000000000000 --- a/ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib -{ - /// - /// Indicates that the input stream could not decoded due to the stream ending before enough data had been provided - /// - [Serializable] - public class UnexpectedEndOfStreamException : StreamDecodingException - { - private const string GenericMessage = "Input stream ended unexpectedly"; - - /// - /// Initializes a new instance of the UnexpectedEndOfStreamException with a generic message - /// - public UnexpectedEndOfStreamException() : base(GenericMessage) { } - - /// - /// Initializes a new instance of the UnexpectedEndOfStreamException class with a specified error message. - /// - /// A message describing the exception. - public UnexpectedEndOfStreamException(string message) : base(message) { } - - /// - /// Initializes a new instance of the UnexpectedEndOfStreamException class with a specified - /// error message and a reference to the inner exception that is the cause of this exception. - /// - /// A message describing the exception. - /// The inner exception - public UnexpectedEndOfStreamException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the UnexpectedEndOfStreamException class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected UnexpectedEndOfStreamException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs b/ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs deleted file mode 100644 index d41cf9882750..000000000000 --- a/ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib -{ - /// - /// Indicates that a value was outside of the expected range when decoding an input stream - /// - [Serializable] - public class ValueOutOfRangeException : StreamDecodingException - { - /// - /// Initializes a new instance of the ValueOutOfRangeException class naming the causing variable - /// - /// Name of the variable, use: nameof() - public ValueOutOfRangeException(string nameOfValue) - : base($"{nameOfValue} out of range") { } - - /// - /// Initializes a new instance of the ValueOutOfRangeException class naming the causing variable, - /// it's current value and expected range. - /// - /// Name of the variable, use: nameof() - /// The invalid value - /// Expected maximum value - /// Expected minimum value - public ValueOutOfRangeException(string nameOfValue, long value, long maxValue, long minValue = 0) - : this(nameOfValue, value.ToString(), maxValue.ToString(), minValue.ToString()) { } - - /// - /// Initializes a new instance of the ValueOutOfRangeException class naming the causing variable, - /// it's current value and expected range. - /// - /// Name of the variable, use: nameof() - /// The invalid value - /// Expected maximum value - /// Expected minimum value - public ValueOutOfRangeException(string nameOfValue, string value, string maxValue, string minValue = "0") : - base($"{nameOfValue} out of range: {value}, should be {minValue}..{maxValue}") - { } - - private ValueOutOfRangeException() - { - } - - private ValueOutOfRangeException(string message, Exception innerException) : base(message, innerException) - { - } - - /// - /// Initializes a new instance of the ValueOutOfRangeException class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected ValueOutOfRangeException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Core/FileSystemScanner.cs b/ICSharpCode.SharpZipLib/Core/FileSystemScanner.cs deleted file mode 100644 index 427e7d895c3b..000000000000 --- a/ICSharpCode.SharpZipLib/Core/FileSystemScanner.cs +++ /dev/null @@ -1,545 +0,0 @@ -using System; - -namespace ICSharpCode.SharpZipLib.Core -{ - #region EventArgs - - /// - /// Event arguments for scanning. - /// - public class ScanEventArgs : EventArgs - { - #region Constructors - - /// - /// Initialise a new instance of - /// - /// The file or directory name. - public ScanEventArgs(string name) - { - name_ = name; - } - - #endregion Constructors - - /// - /// The file or directory name for this event. - /// - public string Name - { - get { return name_; } - } - - /// - /// Get set a value indicating if scanning should continue or not. - /// - public bool ContinueRunning - { - get { return continueRunning_; } - set { continueRunning_ = value; } - } - - #region Instance Fields - - private string name_; - private bool continueRunning_ = true; - - #endregion Instance Fields - } - - /// - /// Event arguments during processing of a single file or directory. - /// - public class ProgressEventArgs : EventArgs - { - #region Constructors - - /// - /// Initialise a new instance of - /// - /// The file or directory name if known. - /// The number of bytes processed so far - /// The total number of bytes to process, 0 if not known - public ProgressEventArgs(string name, long processed, long target) - { - name_ = name; - processed_ = processed; - target_ = target; - } - - #endregion Constructors - - /// - /// The name for this event if known. - /// - public string Name - { - get { return name_; } - } - - /// - /// Get set a value indicating whether scanning should continue or not. - /// - public bool ContinueRunning - { - get { return continueRunning_; } - set { continueRunning_ = value; } - } - - /// - /// Get a percentage representing how much of the has been processed - /// - /// 0.0 to 100.0 percent; 0 if target is not known. - public float PercentComplete - { - get - { - float result; - if (target_ <= 0) - { - result = 0; - } - else - { - result = ((float)processed_ / (float)target_) * 100.0f; - } - return result; - } - } - - /// - /// The number of bytes processed so far - /// - public long Processed - { - get { return processed_; } - } - - /// - /// The number of bytes to process. - /// - /// Target may be 0 or negative if the value isnt known. - public long Target - { - get { return target_; } - } - - #region Instance Fields - - private string name_; - private long processed_; - private long target_; - private bool continueRunning_ = true; - - #endregion Instance Fields - } - - /// - /// Event arguments for directories. - /// - public class DirectoryEventArgs : ScanEventArgs - { - #region Constructors - - /// - /// Initialize an instance of . - /// - /// The name for this directory. - /// Flag value indicating if any matching files are contained in this directory. - public DirectoryEventArgs(string name, bool hasMatchingFiles) - : base(name) - { - hasMatchingFiles_ = hasMatchingFiles; - } - - #endregion Constructors - - /// - /// Get a value indicating if the directory contains any matching files or not. - /// - public bool HasMatchingFiles - { - get { return hasMatchingFiles_; } - } - - private readonly - - #region Instance Fields - - bool hasMatchingFiles_; - - #endregion Instance Fields - } - - /// - /// Arguments passed when scan failures are detected. - /// - public class ScanFailureEventArgs : EventArgs - { - #region Constructors - - /// - /// Initialise a new instance of - /// - /// The name to apply. - /// The exception to use. - public ScanFailureEventArgs(string name, Exception e) - { - name_ = name; - exception_ = e; - continueRunning_ = true; - } - - #endregion Constructors - - /// - /// The applicable name. - /// - public string Name - { - get { return name_; } - } - - /// - /// The applicable exception. - /// - public Exception Exception - { - get { return exception_; } - } - - /// - /// Get / set a value indicating whether scanning should continue. - /// - public bool ContinueRunning - { - get { return continueRunning_; } - set { continueRunning_ = value; } - } - - #region Instance Fields - - private string name_; - private Exception exception_; - private bool continueRunning_; - - #endregion Instance Fields - } - - #endregion EventArgs - - #region Delegates - - /// - /// Delegate invoked before starting to process a file. - /// - /// The source of the event - /// The event arguments. - public delegate void ProcessFileHandler(object sender, ScanEventArgs e); - - /// - /// Delegate invoked during processing of a file or directory - /// - /// The source of the event - /// The event arguments. - public delegate void ProgressHandler(object sender, ProgressEventArgs e); - - /// - /// Delegate invoked when a file has been completely processed. - /// - /// The source of the event - /// The event arguments. - public delegate void CompletedFileHandler(object sender, ScanEventArgs e); - - /// - /// Delegate invoked when a directory failure is detected. - /// - /// The source of the event - /// The event arguments. - public delegate void DirectoryFailureHandler(object sender, ScanFailureEventArgs e); - - /// - /// Delegate invoked when a file failure is detected. - /// - /// The source of the event - /// The event arguments. - public delegate void FileFailureHandler(object sender, ScanFailureEventArgs e); - - #endregion Delegates - - /// - /// FileSystemScanner provides facilities scanning of files and directories. - /// - public class FileSystemScanner - { - #region Constructors - - /// - /// Initialise a new instance of - /// - /// The file filter to apply when scanning. - public FileSystemScanner(string filter) - { - fileFilter_ = new PathFilter(filter); - } - - /// - /// Initialise a new instance of - /// - /// The file filter to apply. - /// The directory filter to apply. - public FileSystemScanner(string fileFilter, string directoryFilter) - { - fileFilter_ = new PathFilter(fileFilter); - directoryFilter_ = new PathFilter(directoryFilter); - } - - /// - /// Initialise a new instance of - /// - /// The file filter to apply. - public FileSystemScanner(IScanFilter fileFilter) - { - fileFilter_ = fileFilter; - } - - /// - /// Initialise a new instance of - /// - /// The file filter to apply. - /// The directory filter to apply. - public FileSystemScanner(IScanFilter fileFilter, IScanFilter directoryFilter) - { - fileFilter_ = fileFilter; - directoryFilter_ = directoryFilter; - } - - #endregion Constructors - - #region Delegates - - /// - /// Delegate to invoke when a directory is processed. - /// - public event EventHandler ProcessDirectory; - - /// - /// Delegate to invoke when a file is processed. - /// - public ProcessFileHandler ProcessFile; - - /// - /// Delegate to invoke when processing for a file has finished. - /// - public CompletedFileHandler CompletedFile; - - /// - /// Delegate to invoke when a directory failure is detected. - /// - public DirectoryFailureHandler DirectoryFailure; - - /// - /// Delegate to invoke when a file failure is detected. - /// - public FileFailureHandler FileFailure; - - #endregion Delegates - - /// - /// Raise the DirectoryFailure event. - /// - /// The directory name. - /// The exception detected. - private bool OnDirectoryFailure(string directory, Exception e) - { - DirectoryFailureHandler handler = DirectoryFailure; - bool result = (handler != null); - if (result) - { - var args = new ScanFailureEventArgs(directory, e); - handler(this, args); - alive_ = args.ContinueRunning; - } - return result; - } - - /// - /// Raise the FileFailure event. - /// - /// The file name. - /// The exception detected. - private bool OnFileFailure(string file, Exception e) - { - FileFailureHandler handler = FileFailure; - - bool result = (handler != null); - - if (result) - { - var args = new ScanFailureEventArgs(file, e); - FileFailure(this, args); - alive_ = args.ContinueRunning; - } - return result; - } - - /// - /// Raise the ProcessFile event. - /// - /// The file name. - private void OnProcessFile(string file) - { - ProcessFileHandler handler = ProcessFile; - - if (handler != null) - { - var args = new ScanEventArgs(file); - handler(this, args); - alive_ = args.ContinueRunning; - } - } - - /// - /// Raise the complete file event - /// - /// The file name - private void OnCompleteFile(string file) - { - CompletedFileHandler handler = CompletedFile; - - if (handler != null) - { - var args = new ScanEventArgs(file); - handler(this, args); - alive_ = args.ContinueRunning; - } - } - - /// - /// Raise the ProcessDirectory event. - /// - /// The directory name. - /// Flag indicating if the directory has matching files. - private void OnProcessDirectory(string directory, bool hasMatchingFiles) - { - EventHandler handler = ProcessDirectory; - - if (handler != null) - { - var args = new DirectoryEventArgs(directory, hasMatchingFiles); - handler(this, args); - alive_ = args.ContinueRunning; - } - } - - /// - /// Scan a directory. - /// - /// The base directory to scan. - /// True to recurse subdirectories, false to scan a single directory. - public void Scan(string directory, bool recurse) - { - alive_ = true; - ScanDir(directory, recurse); - } - - private void ScanDir(string directory, bool recurse) - { - try - { - string[] names = System.IO.Directory.GetFiles(directory); - bool hasMatch = false; - for (int fileIndex = 0; fileIndex < names.Length; ++fileIndex) - { - if (!fileFilter_.IsMatch(names[fileIndex])) - { - names[fileIndex] = null; - } - else - { - hasMatch = true; - } - } - - OnProcessDirectory(directory, hasMatch); - - if (alive_ && hasMatch) - { - foreach (string fileName in names) - { - try - { - if (fileName != null) - { - OnProcessFile(fileName); - if (!alive_) - { - break; - } - } - } - catch (Exception e) - { - if (!OnFileFailure(fileName, e)) - { - throw; - } - } - } - } - } - catch (Exception e) - { - if (!OnDirectoryFailure(directory, e)) - { - throw; - } - } - - if (alive_ && recurse) - { - try - { - string[] names = System.IO.Directory.GetDirectories(directory); - foreach (string fulldir in names) - { - if ((directoryFilter_ == null) || (directoryFilter_.IsMatch(fulldir))) - { - ScanDir(fulldir, true); - if (!alive_) - { - break; - } - } - } - } - catch (Exception e) - { - if (!OnDirectoryFailure(directory, e)) - { - throw; - } - } - } - } - - #region Instance Fields - - /// - /// The file filter currently in use. - /// - private IScanFilter fileFilter_; - - /// - /// The directory filter currently in use. - /// - private IScanFilter directoryFilter_; - - /// - /// Flag indicating if scanning should continue running. - /// - private bool alive_; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Core/INameTransform.cs b/ICSharpCode.SharpZipLib/Core/INameTransform.cs deleted file mode 100644 index 492e2a9e6f06..000000000000 --- a/ICSharpCode.SharpZipLib/Core/INameTransform.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace ICSharpCode.SharpZipLib.Core -{ - /// - /// INameTransform defines how file system names are transformed for use with archives, or vice versa. - /// - public interface INameTransform - { - /// - /// Given a file name determine the transformed value. - /// - /// The name to transform. - /// The transformed file name. - string TransformFile(string name); - - /// - /// Given a directory name determine the transformed value. - /// - /// The name to transform. - /// The transformed directory name - string TransformDirectory(string name); - } -} diff --git a/ICSharpCode.SharpZipLib/Core/IScanFilter.cs b/ICSharpCode.SharpZipLib/Core/IScanFilter.cs deleted file mode 100644 index ac07fd174149..000000000000 --- a/ICSharpCode.SharpZipLib/Core/IScanFilter.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace ICSharpCode.SharpZipLib.Core -{ - /// - /// Scanning filters support filtering of names. - /// - public interface IScanFilter - { - /// - /// Test a name to see if it 'matches' the filter. - /// - /// The name to test. - /// Returns true if the name matches the filter, false if it does not match. - bool IsMatch(string name); - } -} diff --git a/ICSharpCode.SharpZipLib/Core/InvalidNameException.cs b/ICSharpCode.SharpZipLib/Core/InvalidNameException.cs deleted file mode 100644 index 6647631bdd79..000000000000 --- a/ICSharpCode.SharpZipLib/Core/InvalidNameException.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib.Core -{ - /// - /// InvalidNameException is thrown for invalid names such as directory traversal paths and names with invalid characters - /// - [Serializable] - public class InvalidNameException : SharpZipBaseException - { - /// - /// Initializes a new instance of the InvalidNameException class with a default error message. - /// - public InvalidNameException() : base("An invalid name was specified") - { - } - - /// - /// Initializes a new instance of the InvalidNameException class with a specified error message. - /// - /// A message describing the exception. - public InvalidNameException(string message) : base(message) - { - } - - /// - /// Initializes a new instance of the InvalidNameException class with a specified - /// error message and a reference to the inner exception that is the cause of this exception. - /// - /// A message describing the exception. - /// The inner exception - public InvalidNameException(string message, Exception innerException) : base(message, innerException) - { - } - - /// - /// Initializes a new instance of the InvalidNameException class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected InvalidNameException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Core/NameFilter.cs b/ICSharpCode.SharpZipLib/Core/NameFilter.cs deleted file mode 100644 index 57751891c657..000000000000 --- a/ICSharpCode.SharpZipLib/Core/NameFilter.cs +++ /dev/null @@ -1,284 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - -namespace ICSharpCode.SharpZipLib.Core -{ - /// - /// NameFilter is a string matching class which allows for both positive and negative - /// matching. - /// A filter is a sequence of independant regular expressions separated by semi-colons ';'. - /// To include a semi-colon it may be quoted as in \;. Each expression can be prefixed by a plus '+' sign or - /// a minus '-' sign to denote the expression is intended to include or exclude names. - /// If neither a plus or minus sign is found include is the default. - /// A given name is tested for inclusion before checking exclusions. Only names matching an include spec - /// and not matching an exclude spec are deemed to match the filter. - /// An empty filter matches any name. - /// - /// The following expression includes all name ending in '.dat' with the exception of 'dummy.dat' - /// "+\.dat$;-^dummy\.dat$" - /// - public class NameFilter : IScanFilter - { - #region Constructors - - /// - /// Construct an instance based on the filter expression passed - /// - /// The filter expression. - public NameFilter(string filter) - { - filter_ = filter; - inclusions_ = new List(); - exclusions_ = new List(); - Compile(); - } - - #endregion Constructors - - /// - /// Test a string to see if it is a valid regular expression. - /// - /// The expression to test. - /// True if expression is a valid false otherwise. - public static bool IsValidExpression(string expression) - { - bool result = true; - try - { - var exp = new Regex(expression, RegexOptions.IgnoreCase | RegexOptions.Singleline); - } - catch (ArgumentException) - { - result = false; - } - return result; - } - - /// - /// Test an expression to see if it is valid as a filter. - /// - /// The filter expression to test. - /// True if the expression is valid, false otherwise. - public static bool IsValidFilterExpression(string toTest) - { - bool result = true; - - try - { - if (toTest != null) - { - string[] items = SplitQuoted(toTest); - for (int i = 0; i < items.Length; ++i) - { - if ((items[i] != null) && (items[i].Length > 0)) - { - string toCompile; - - if (items[i][0] == '+') - { - toCompile = items[i].Substring(1, items[i].Length - 1); - } - else if (items[i][0] == '-') - { - toCompile = items[i].Substring(1, items[i].Length - 1); - } - else - { - toCompile = items[i]; - } - - var testRegex = new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Singleline); - } - } - } - } - catch (ArgumentException) - { - result = false; - } - - return result; - } - - /// - /// Split a string into its component pieces - /// - /// The original string - /// Returns an array of values containing the individual filter elements. - public static string[] SplitQuoted(string original) - { - char escape = '\\'; - char[] separators = { ';' }; - - var result = new List(); - - if (!string.IsNullOrEmpty(original)) - { - int endIndex = -1; - var b = new StringBuilder(); - - while (endIndex < original.Length) - { - endIndex += 1; - if (endIndex >= original.Length) - { - result.Add(b.ToString()); - } - else if (original[endIndex] == escape) - { - endIndex += 1; - if (endIndex >= original.Length) - { - throw new ArgumentException("Missing terminating escape character", nameof(original)); - } - // include escape if this is not an escaped separator - if (Array.IndexOf(separators, original[endIndex]) < 0) - b.Append(escape); - - b.Append(original[endIndex]); - } - else - { - if (Array.IndexOf(separators, original[endIndex]) >= 0) - { - result.Add(b.ToString()); - b.Length = 0; - } - else - { - b.Append(original[endIndex]); - } - } - } - } - - return result.ToArray(); - } - - /// - /// Convert this filter to its string equivalent. - /// - /// The string equivalent for this filter. - public override string ToString() - { - return filter_; - } - - /// - /// Test a value to see if it is included by the filter. - /// - /// The value to test. - /// True if the value is included, false otherwise. - public bool IsIncluded(string name) - { - bool result = false; - if (inclusions_.Count == 0) - { - result = true; - } - else - { - foreach (Regex r in inclusions_) - { - if (r.IsMatch(name)) - { - result = true; - break; - } - } - } - return result; - } - - /// - /// Test a value to see if it is excluded by the filter. - /// - /// The value to test. - /// True if the value is excluded, false otherwise. - public bool IsExcluded(string name) - { - bool result = false; - foreach (Regex r in exclusions_) - { - if (r.IsMatch(name)) - { - result = true; - break; - } - } - return result; - } - - #region IScanFilter Members - - /// - /// Test a value to see if it matches the filter. - /// - /// The value to test. - /// True if the value matches, false otherwise. - public bool IsMatch(string name) - { - return (IsIncluded(name) && !IsExcluded(name)); - } - - #endregion IScanFilter Members - - /// - /// Compile this filter. - /// - private void Compile() - { - // TODO: Check to see if combining RE's makes it faster/smaller. - // simple scheme would be to have one RE for inclusion and one for exclusion. - if (filter_ == null) - { - return; - } - - string[] items = SplitQuoted(filter_); - for (int i = 0; i < items.Length; ++i) - { - if ((items[i] != null) && (items[i].Length > 0)) - { - bool include = (items[i][0] != '-'); - string toCompile; - - if (items[i][0] == '+') - { - toCompile = items[i].Substring(1, items[i].Length - 1); - } - else if (items[i][0] == '-') - { - toCompile = items[i].Substring(1, items[i].Length - 1); - } - else - { - toCompile = items[i]; - } - - // NOTE: Regular expressions can fail to compile here for a number of reasons that cause an exception - // these are left unhandled here as the caller is responsible for ensuring all is valid. - // several functions IsValidFilterExpression and IsValidExpression are provided for such checking - if (include) - { - inclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)); - } - else - { - exclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)); - } - } - } - } - - #region Instance Fields - - private string filter_; - private List inclusions_; - private List exclusions_; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Core/PathFilter.cs b/ICSharpCode.SharpZipLib/Core/PathFilter.cs deleted file mode 100644 index e70109c2c99c..000000000000 --- a/ICSharpCode.SharpZipLib/Core/PathFilter.cs +++ /dev/null @@ -1,318 +0,0 @@ -using System; -using System.IO; - -namespace ICSharpCode.SharpZipLib.Core -{ - /// - /// PathFilter filters directories and files using a form of regular expressions - /// by full path name. - /// See NameFilter for more detail on filtering. - /// - public class PathFilter : IScanFilter - { - #region Constructors - - /// - /// Initialise a new instance of . - /// - /// The filter expression to apply. - public PathFilter(string filter) - { - nameFilter_ = new NameFilter(filter); - } - - #endregion Constructors - - #region IScanFilter Members - - /// - /// Test a name to see if it matches the filter. - /// - /// The name to test. - /// True if the name matches, false otherwise. - /// is used to get the full path before matching. - public virtual bool IsMatch(string name) - { - bool result = false; - - if (name != null) - { - string cooked = (name.Length > 0) ? Path.GetFullPath(name) : ""; - result = nameFilter_.IsMatch(cooked); - } - return result; - } - - private readonly - - #endregion IScanFilter Members - - #region Instance Fields - - NameFilter nameFilter_; - - #endregion Instance Fields - } - - /// - /// ExtendedPathFilter filters based on name, file size, and the last write time of the file. - /// - /// Provides an example of how to customise filtering. - public class ExtendedPathFilter : PathFilter - { - #region Constructors - - /// - /// Initialise a new instance of ExtendedPathFilter. - /// - /// The filter to apply. - /// The minimum file size to include. - /// The maximum file size to include. - public ExtendedPathFilter(string filter, - long minSize, long maxSize) - : base(filter) - { - MinSize = minSize; - MaxSize = maxSize; - } - - /// - /// Initialise a new instance of ExtendedPathFilter. - /// - /// The filter to apply. - /// The minimum to include. - /// The maximum to include. - public ExtendedPathFilter(string filter, - DateTime minDate, DateTime maxDate) - : base(filter) - { - MinDate = minDate; - MaxDate = maxDate; - } - - /// - /// Initialise a new instance of ExtendedPathFilter. - /// - /// The filter to apply. - /// The minimum file size to include. - /// The maximum file size to include. - /// The minimum to include. - /// The maximum to include. - public ExtendedPathFilter(string filter, - long minSize, long maxSize, - DateTime minDate, DateTime maxDate) - : base(filter) - { - MinSize = minSize; - MaxSize = maxSize; - MinDate = minDate; - MaxDate = maxDate; - } - - #endregion Constructors - - #region IScanFilter Members - - /// - /// Test a filename to see if it matches the filter. - /// - /// The filename to test. - /// True if the filter matches, false otherwise. - /// The doesnt exist - public override bool IsMatch(string name) - { - bool result = base.IsMatch(name); - - if (result) - { - var fileInfo = new FileInfo(name); - result = - (MinSize <= fileInfo.Length) && - (MaxSize >= fileInfo.Length) && - (MinDate <= fileInfo.LastWriteTime) && - (MaxDate >= fileInfo.LastWriteTime) - ; - } - return result; - } - - #endregion IScanFilter Members - - #region Properties - - /// - /// Get/set the minimum size/length for a file that will match this filter. - /// - /// The default value is zero. - /// value is less than zero; greater than - public long MinSize - { - get { return minSize_; } - set - { - if ((value < 0) || (maxSize_ < value)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - - minSize_ = value; - } - } - - /// - /// Get/set the maximum size/length for a file that will match this filter. - /// - /// The default value is - /// value is less than zero or less than - public long MaxSize - { - get { return maxSize_; } - set - { - if ((value < 0) || (minSize_ > value)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - - maxSize_ = value; - } - } - - /// - /// Get/set the minimum value that will match for this filter. - /// - /// Files with a LastWrite time less than this value are excluded by the filter. - public DateTime MinDate - { - get - { - return minDate_; - } - - set - { - if (value > maxDate_) - { - throw new ArgumentOutOfRangeException(nameof(value), "Exceeds MaxDate"); - } - - minDate_ = value; - } - } - - /// - /// Get/set the maximum value that will match for this filter. - /// - /// Files with a LastWrite time greater than this value are excluded by the filter. - public DateTime MaxDate - { - get - { - return maxDate_; - } - - set - { - if (minDate_ > value) - { - throw new ArgumentOutOfRangeException(nameof(value), "Exceeds MinDate"); - } - - maxDate_ = value; - } - } - - #endregion Properties - - #region Instance Fields - - private long minSize_; - private long maxSize_ = long.MaxValue; - private DateTime minDate_ = DateTime.MinValue; - private DateTime maxDate_ = DateTime.MaxValue; - - #endregion Instance Fields - } - - /// - /// NameAndSizeFilter filters based on name and file size. - /// - /// A sample showing how filters might be extended. - [Obsolete("Use ExtendedPathFilter instead")] - public class NameAndSizeFilter : PathFilter - { - /// - /// Initialise a new instance of NameAndSizeFilter. - /// - /// The filter to apply. - /// The minimum file size to include. - /// The maximum file size to include. - public NameAndSizeFilter(string filter, long minSize, long maxSize) - : base(filter) - { - MinSize = minSize; - MaxSize = maxSize; - } - - /// - /// Test a filename to see if it matches the filter. - /// - /// The filename to test. - /// True if the filter matches, false otherwise. - public override bool IsMatch(string name) - { - bool result = base.IsMatch(name); - - if (result) - { - var fileInfo = new FileInfo(name); - long length = fileInfo.Length; - result = - (MinSize <= length) && - (MaxSize >= length); - } - return result; - } - - /// - /// Get/set the minimum size for a file that will match this filter. - /// - public long MinSize - { - get { return minSize_; } - set - { - if ((value < 0) || (maxSize_ < value)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - - minSize_ = value; - } - } - - /// - /// Get/set the maximum size for a file that will match this filter. - /// - public long MaxSize - { - get { return maxSize_; } - set - { - if ((value < 0) || (minSize_ > value)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - - maxSize_ = value; - } - } - - #region Instance Fields - - private long minSize_; - private long maxSize_ = long.MaxValue; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Core/PathUtils.cs b/ICSharpCode.SharpZipLib/Core/PathUtils.cs deleted file mode 100644 index b8d0dd409f9f..000000000000 --- a/ICSharpCode.SharpZipLib/Core/PathUtils.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.IO; -using System.Linq; - -namespace ICSharpCode.SharpZipLib.Core -{ - /// - /// PathUtils provides simple utilities for handling paths. - /// - public static class PathUtils - { - /// - /// Remove any path root present in the path - /// - /// A containing path information. - /// The path with the root removed if it was present; path otherwise. - public static string DropPathRoot(string path) - { - var invalidChars = Path.GetInvalidPathChars(); - // If the first character after the root is a ':', .NET < 4.6.2 throws - var cleanRootSep = path.Length >= 3 && path[1] == ':' && path[2] == ':'; - - // Replace any invalid path characters with '_' to prevent Path.GetPathRoot from throwing. - // Only pass the first 258 (should be 260, but that still throws for some reason) characters - // as .NET < 4.6.2 throws on longer paths - var cleanPath = new string(path.Take(258) - .Select( (c, i) => invalidChars.Contains(c) || (i == 2 && cleanRootSep) ? '_' : c).ToArray()); - - var stripLength = Path.GetPathRoot(cleanPath).Length; - while (path.Length > stripLength && (path[stripLength] == '/' || path[stripLength] == '\\')) stripLength++; - return path.Substring(stripLength); - } - - /// - /// Returns a random file name in the users temporary directory, or in directory of if specified - /// - /// If specified, used as the base file name for the temporary file - /// Returns a temporary file name - public static string GetTempFileName(string original = null) - { - string fileName; - var tempPath = Path.GetTempPath(); - - do - { - fileName = original == null - ? Path.Combine(tempPath, Path.GetRandomFileName()) - : $"{original}.{Path.GetRandomFileName()}"; - } while (File.Exists(fileName)); - - return fileName; - } - } -} diff --git a/ICSharpCode.SharpZipLib/Core/StreamUtils.cs b/ICSharpCode.SharpZipLib/Core/StreamUtils.cs deleted file mode 100644 index 47de6e26ecd5..000000000000 --- a/ICSharpCode.SharpZipLib/Core/StreamUtils.cs +++ /dev/null @@ -1,295 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace ICSharpCode.SharpZipLib.Core -{ - /// - /// Provides simple " utilities. - /// - public static class StreamUtils - { - /// - /// Read from a ensuring all the required data is read. - /// - /// The stream to read. - /// The buffer to fill. - /// - public static void ReadFully(Stream stream, byte[] buffer) - { - ReadFully(stream, buffer, 0, buffer.Length); - } - - /// - /// Read from a " ensuring all the required data is read. - /// - /// The stream to read data from. - /// The buffer to store data in. - /// The offset at which to begin storing data. - /// The number of bytes of data to store. - /// Required parameter is null - /// and or are invalid. - /// End of stream is encountered before all the data has been read. - public static void ReadFully(Stream stream, byte[] buffer, int offset, int count) - { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - // Offset can equal length when buffer and count are 0. - if ((offset < 0) || (offset > buffer.Length)) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - if ((count < 0) || (offset + count > buffer.Length)) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - while (count > 0) - { - int readCount = stream.Read(buffer, offset, count); - if (readCount <= 0) - { - throw new EndOfStreamException(); - } - offset += readCount; - count -= readCount; - } - } - - /// - /// Read as much data as possible from a ", up to the requested number of bytes - /// - /// The stream to read data from. - /// The buffer to store data in. - /// The offset at which to begin storing data. - /// The number of bytes of data to store. - /// Required parameter is null - /// and or are invalid. - public static int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, int count) - { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - // Offset can equal length when buffer and count are 0. - if ((offset < 0) || (offset > buffer.Length)) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - if ((count < 0) || (offset + count > buffer.Length)) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - int totalReadCount = 0; - while (count > 0) - { - int readCount = stream.Read(buffer, offset, count); - if (readCount <= 0) - { - break; - } - offset += readCount; - count -= readCount; - totalReadCount += readCount; - } - - return totalReadCount; - } - - /// - /// Copy the contents of one to another. - /// - /// The stream to source data from. - /// The stream to write data to. - /// The buffer to use during copying. - public static void Copy(Stream source, Stream destination, byte[] buffer) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - if (destination == null) - { - throw new ArgumentNullException(nameof(destination)); - } - - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - // Ensure a reasonable size of buffer is used without being prohibitive. - if (buffer.Length < 128) - { - throw new ArgumentException("Buffer is too small", nameof(buffer)); - } - - bool copying = true; - - while (copying) - { - int bytesRead = source.Read(buffer, 0, buffer.Length); - if (bytesRead > 0) - { - destination.Write(buffer, 0, bytesRead); - } - else - { - destination.Flush(); - copying = false; - } - } - } - - /// - /// Copy the contents of one to another. - /// - /// The stream to source data from. - /// The stream to write data to. - /// The buffer to use during copying. - /// The progress handler delegate to use. - /// The minimum between progress updates. - /// The source for this event. - /// The name to use with the event. - /// This form is specialised for use within #Zip to support events during archive operations. - public static void Copy(Stream source, Stream destination, - byte[] buffer, ProgressHandler progressHandler, TimeSpan updateInterval, object sender, string name) - { - Copy(source, destination, buffer, progressHandler, updateInterval, sender, name, -1); - } - - /// - /// Copy the contents of one to another. - /// - /// The stream to source data from. - /// The stream to write data to. - /// The buffer to use during copying. - /// The progress handler delegate to use. - /// The minimum between progress updates. - /// The source for this event. - /// The name to use with the event. - /// A predetermined fixed target value to use with progress updates. - /// If the value is negative the target is calculated by looking at the stream. - /// This form is specialised for use within #Zip to support events during archive operations. - public static void Copy(Stream source, Stream destination, - byte[] buffer, - ProgressHandler progressHandler, TimeSpan updateInterval, - object sender, string name, long fixedTarget) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - if (destination == null) - { - throw new ArgumentNullException(nameof(destination)); - } - - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - // Ensure a reasonable size of buffer is used without being prohibitive. - if (buffer.Length < 128) - { - throw new ArgumentException("Buffer is too small", nameof(buffer)); - } - - if (progressHandler == null) - { - throw new ArgumentNullException(nameof(progressHandler)); - } - - bool copying = true; - - DateTime marker = DateTime.Now; - long processed = 0; - long target = 0; - - if (fixedTarget >= 0) - { - target = fixedTarget; - } - else if (source.CanSeek) - { - target = source.Length - source.Position; - } - - // Always fire 0% progress.. - var args = new ProgressEventArgs(name, processed, target); - progressHandler(sender, args); - - bool progressFired = true; - - while (copying) - { - int bytesRead = source.Read(buffer, 0, buffer.Length); - if (bytesRead > 0) - { - processed += bytesRead; - progressFired = false; - destination.Write(buffer, 0, bytesRead); - } - else - { - destination.Flush(); - copying = false; - } - - if (DateTime.Now - marker > updateInterval) - { - progressFired = true; - marker = DateTime.Now; - args = new ProgressEventArgs(name, processed, target); - progressHandler(sender, args); - - copying = args.ContinueRunning; - } - } - - if (!progressFired) - { - args = new ProgressEventArgs(name, processed, target); - progressHandler(sender, args); - } - } - - internal static async Task WriteProcToStreamAsync(this Stream targetStream, MemoryStream bufferStream, Action writeProc, CancellationToken ct) - { - bufferStream.SetLength(0); - writeProc(bufferStream); - bufferStream.Position = 0; - await bufferStream.CopyToAsync(targetStream, 81920, ct); - bufferStream.SetLength(0); - } - - internal static async Task WriteProcToStreamAsync(this Stream targetStream, Action writeProc, CancellationToken ct) - { - using (var ms = new MemoryStream()) - { - await WriteProcToStreamAsync(targetStream, ms, writeProc, ct); - } - } - } -} diff --git a/ICSharpCode.SharpZipLib/Encryption/PkzipClassic.cs b/ICSharpCode.SharpZipLib/Encryption/PkzipClassic.cs deleted file mode 100644 index 6730c9dee181..000000000000 --- a/ICSharpCode.SharpZipLib/Encryption/PkzipClassic.cs +++ /dev/null @@ -1,487 +0,0 @@ -using ICSharpCode.SharpZipLib.Checksum; -using System; -using System.Security.Cryptography; - -namespace ICSharpCode.SharpZipLib.Encryption -{ - /// - /// PkzipClassic embodies the classic or original encryption facilities used in Pkzip archives. - /// While it has been superceded by more recent and more powerful algorithms, its still in use and - /// is viable for preventing casual snooping - /// - public abstract class PkzipClassic : SymmetricAlgorithm - { - /// - /// Generates new encryption keys based on given seed - /// - /// The seed value to initialise keys with. - /// A new key value. - static public byte[] GenerateKeys(byte[] seed) - { - if (seed == null) - { - throw new ArgumentNullException(nameof(seed)); - } - - if (seed.Length == 0) - { - throw new ArgumentException("Length is zero", nameof(seed)); - } - - uint[] newKeys = { - 0x12345678, - 0x23456789, - 0x34567890 - }; - - for (int i = 0; i < seed.Length; ++i) - { - newKeys[0] = Crc32.ComputeCrc32(newKeys[0], seed[i]); - newKeys[1] = newKeys[1] + (byte)newKeys[0]; - newKeys[1] = newKeys[1] * 134775813 + 1; - newKeys[2] = Crc32.ComputeCrc32(newKeys[2], (byte)(newKeys[1] >> 24)); - } - - byte[] result = new byte[12]; - result[0] = (byte)(newKeys[0] & 0xff); - result[1] = (byte)((newKeys[0] >> 8) & 0xff); - result[2] = (byte)((newKeys[0] >> 16) & 0xff); - result[3] = (byte)((newKeys[0] >> 24) & 0xff); - result[4] = (byte)(newKeys[1] & 0xff); - result[5] = (byte)((newKeys[1] >> 8) & 0xff); - result[6] = (byte)((newKeys[1] >> 16) & 0xff); - result[7] = (byte)((newKeys[1] >> 24) & 0xff); - result[8] = (byte)(newKeys[2] & 0xff); - result[9] = (byte)((newKeys[2] >> 8) & 0xff); - result[10] = (byte)((newKeys[2] >> 16) & 0xff); - result[11] = (byte)((newKeys[2] >> 24) & 0xff); - return result; - } - } - - /// - /// PkzipClassicCryptoBase provides the low level facilities for encryption - /// and decryption using the PkzipClassic algorithm. - /// - internal class PkzipClassicCryptoBase - { - /// - /// Transform a single byte - /// - /// - /// The transformed value - /// - protected byte TransformByte() - { - uint temp = ((keys[2] & 0xFFFF) | 2); - return (byte)((temp * (temp ^ 1)) >> 8); - } - - /// - /// Set the key schedule for encryption/decryption. - /// - /// The data use to set the keys from. - protected void SetKeys(byte[] keyData) - { - if (keyData == null) - { - throw new ArgumentNullException(nameof(keyData)); - } - - if (keyData.Length != 12) - { - throw new InvalidOperationException("Key length is not valid"); - } - - keys = new uint[3]; - keys[0] = (uint)((keyData[3] << 24) | (keyData[2] << 16) | (keyData[1] << 8) | keyData[0]); - keys[1] = (uint)((keyData[7] << 24) | (keyData[6] << 16) | (keyData[5] << 8) | keyData[4]); - keys[2] = (uint)((keyData[11] << 24) | (keyData[10] << 16) | (keyData[9] << 8) | keyData[8]); - } - - /// - /// Update encryption keys - /// - protected void UpdateKeys(byte ch) - { - keys[0] = Crc32.ComputeCrc32(keys[0], ch); - keys[1] = keys[1] + (byte)keys[0]; - keys[1] = keys[1] * 134775813 + 1; - keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24)); - } - - /// - /// Reset the internal state. - /// - protected void Reset() - { - keys[0] = 0; - keys[1] = 0; - keys[2] = 0; - } - - #region Instance Fields - - private uint[] keys; - - #endregion Instance Fields - } - - /// - /// PkzipClassic CryptoTransform for encryption. - /// - internal class PkzipClassicEncryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform - { - /// - /// Initialise a new instance of - /// - /// The key block to use. - internal PkzipClassicEncryptCryptoTransform(byte[] keyBlock) - { - SetKeys(keyBlock); - } - - #region ICryptoTransform Members - - /// - /// Transforms the specified region of the specified byte array. - /// - /// The input for which to compute the transform. - /// The offset into the byte array from which to begin using data. - /// The number of bytes in the byte array to use as data. - /// The computed transform. - public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) - { - byte[] result = new byte[inputCount]; - TransformBlock(inputBuffer, inputOffset, inputCount, result, 0); - return result; - } - - /// - /// Transforms the specified region of the input byte array and copies - /// the resulting transform to the specified region of the output byte array. - /// - /// The input for which to compute the transform. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write the transform. - /// The offset into the output byte array from which to begin writing data. - /// The number of bytes written. - public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - for (int i = inputOffset; i < inputOffset + inputCount; ++i) - { - byte oldbyte = inputBuffer[i]; - outputBuffer[outputOffset++] = (byte)(inputBuffer[i] ^ TransformByte()); - UpdateKeys(oldbyte); - } - return inputCount; - } - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - public bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets the size of the input data blocks in bytes. - /// - public int InputBlockSize - { - get - { - return 1; - } - } - - /// - /// Gets the size of the output data blocks in bytes. - /// - public int OutputBlockSize - { - get - { - return 1; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - public bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - #endregion ICryptoTransform Members - - #region IDisposable Members - - /// - /// Cleanup internal state. - /// - public void Dispose() - { - Reset(); - } - - #endregion IDisposable Members - } - - /// - /// PkzipClassic CryptoTransform for decryption. - /// - internal class PkzipClassicDecryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform - { - /// - /// Initialise a new instance of . - /// - /// The key block to decrypt with. - internal PkzipClassicDecryptCryptoTransform(byte[] keyBlock) - { - SetKeys(keyBlock); - } - - #region ICryptoTransform Members - - /// - /// Transforms the specified region of the specified byte array. - /// - /// The input for which to compute the transform. - /// The offset into the byte array from which to begin using data. - /// The number of bytes in the byte array to use as data. - /// The computed transform. - public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) - { - byte[] result = new byte[inputCount]; - TransformBlock(inputBuffer, inputOffset, inputCount, result, 0); - return result; - } - - /// - /// Transforms the specified region of the input byte array and copies - /// the resulting transform to the specified region of the output byte array. - /// - /// The input for which to compute the transform. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write the transform. - /// The offset into the output byte array from which to begin writing data. - /// The number of bytes written. - public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - for (int i = inputOffset; i < inputOffset + inputCount; ++i) - { - var newByte = (byte)(inputBuffer[i] ^ TransformByte()); - outputBuffer[outputOffset++] = newByte; - UpdateKeys(newByte); - } - return inputCount; - } - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - public bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets the size of the input data blocks in bytes. - /// - public int InputBlockSize - { - get - { - return 1; - } - } - - /// - /// Gets the size of the output data blocks in bytes. - /// - public int OutputBlockSize - { - get - { - return 1; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - public bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - #endregion ICryptoTransform Members - - #region IDisposable Members - - /// - /// Cleanup internal state. - /// - public void Dispose() - { - Reset(); - } - - #endregion IDisposable Members - } - - /// - /// Defines a wrapper object to access the Pkzip algorithm. - /// This class cannot be inherited. - /// - public sealed class PkzipClassicManaged : PkzipClassic - { - /// - /// Get / set the applicable block size in bits. - /// - /// The only valid block size is 8. - public override int BlockSize - { - get - { - return 8; - } - - set - { - if (value != 8) - { - throw new CryptographicException("Block size is invalid"); - } - } - } - - /// - /// Get an array of legal key sizes. - /// - public override KeySizes[] LegalKeySizes - { - get - { - KeySizes[] keySizes = new KeySizes[1]; - keySizes[0] = new KeySizes(12 * 8, 12 * 8, 0); - return keySizes; - } - } - - /// - /// Generate an initial vector. - /// - public override void GenerateIV() - { - // Do nothing. - } - - /// - /// Get an array of legal block sizes. - /// - public override KeySizes[] LegalBlockSizes - { - get - { - KeySizes[] keySizes = new KeySizes[1]; - keySizes[0] = new KeySizes(1 * 8, 1 * 8, 0); - return keySizes; - } - } - - /// - /// Get / set the key value applicable. - /// - public override byte[] Key - { - get - { - if (key_ == null) - { - GenerateKey(); - } - - return (byte[])key_.Clone(); - } - - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - if (value.Length != 12) - { - throw new CryptographicException("Key size is illegal"); - } - - key_ = (byte[])value.Clone(); - } - } - - /// - /// Generate a new random key. - /// - public override void GenerateKey() - { - key_ = new byte[12]; - using (var rng = new RNGCryptoServiceProvider()) - { - rng.GetBytes(key_); - } - } - - /// - /// Create an encryptor. - /// - /// The key to use for this encryptor. - /// Initialisation vector for the new encryptor. - /// Returns a new PkzipClassic encryptor - public override ICryptoTransform CreateEncryptor( - byte[] rgbKey, - byte[] rgbIV) - { - key_ = rgbKey; - return new PkzipClassicEncryptCryptoTransform(Key); - } - - /// - /// Create a decryptor. - /// - /// Keys to use for this new decryptor. - /// Initialisation vector for the new decryptor. - /// Returns a new decryptor. - public override ICryptoTransform CreateDecryptor( - byte[] rgbKey, - byte[] rgbIV) - { - key_ = rgbKey; - return new PkzipClassicDecryptCryptoTransform(Key); - } - - #region Instance Fields - - private byte[] key_; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs b/ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs deleted file mode 100644 index 80ce0b4ab237..000000000000 --- a/ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; -using ICSharpCode.SharpZipLib.Core; -using ICSharpCode.SharpZipLib.Zip; - -namespace ICSharpCode.SharpZipLib.Encryption -{ - /// - /// Encrypts and decrypts AES ZIP - /// - /// - /// Based on information from http://www.winzip.com/aes_info.htm - /// and http://www.gladman.me.uk/cryptography_technology/fileencrypt/ - /// - internal class ZipAESStream : CryptoStream - { - /// - /// Constructor - /// - /// The stream on which to perform the cryptographic transformation. - /// Instance of ZipAESTransform - /// Read or Write - public ZipAESStream(Stream stream, ZipAESTransform transform, CryptoStreamMode mode) - : base(stream, transform, mode) - { - _stream = stream; - _transform = transform; - _slideBuffer = new byte[1024]; - - // mode: - // CryptoStreamMode.Read means we read from "stream" and pass decrypted to our Read() method. - // Write bypasses this stream and uses the Transform directly. - if (mode != CryptoStreamMode.Read) - { - throw new Exception("ZipAESStream only for read"); - } - } - - // The final n bytes of the AES stream contain the Auth Code. - private const int AUTH_CODE_LENGTH = 10; - - // Blocksize is always 16 here, even for AES-256 which has transform.InputBlockSize of 32. - private const int CRYPTO_BLOCK_SIZE = 16; - - // total length of block + auth code - private const int BLOCK_AND_AUTH = CRYPTO_BLOCK_SIZE + AUTH_CODE_LENGTH; - - private Stream _stream; - private ZipAESTransform _transform; - private byte[] _slideBuffer; - private int _slideBufStartPos; - private int _slideBufFreePos; - - // Buffer block transforms to enable partial reads - private byte[] _transformBuffer = null;// new byte[CRYPTO_BLOCK_SIZE]; - private int _transformBufferFreePos; - private int _transformBufferStartPos; - - // Do we have some buffered data available? - private bool HasBufferedData =>_transformBuffer != null && _transformBufferStartPos < _transformBufferFreePos; - - /// - /// Reads a sequence of bytes from the current CryptoStream into buffer, - /// and advances the position within the stream by the number of bytes read. - /// - public override int Read(byte[] buffer, int offset, int count) - { - // Nothing to do - if (count == 0) - return 0; - - // If we have buffered data, read that first - int nBytes = 0; - if (HasBufferedData) - { - nBytes = ReadBufferedData(buffer, offset, count); - - // Read all requested data from the buffer - if (nBytes == count) - return nBytes; - - offset += nBytes; - count -= nBytes; - } - - // Read more data from the input, if available - if (_slideBuffer != null) - nBytes += ReadAndTransform(buffer, offset, count); - - return nBytes; - } - - /// - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - var readCount = Read(buffer, offset, count); - return Task.FromResult(readCount); - } - - // Read data from the underlying stream and decrypt it - private int ReadAndTransform(byte[] buffer, int offset, int count) - { - int nBytes = 0; - while (nBytes < count) - { - int bytesLeftToRead = count - nBytes; - - // Calculate buffer quantities vs read-ahead size, and check for sufficient free space - int byteCount = _slideBufFreePos - _slideBufStartPos; - - // Need to handle final block and Auth Code specially, but don't know total data length. - // Maintain a read-ahead equal to the length of (crypto block + Auth Code). - // When that runs out we can detect these final sections. - int lengthToRead = BLOCK_AND_AUTH - byteCount; - if (_slideBuffer.Length - _slideBufFreePos < lengthToRead) - { - // Shift the data to the beginning of the buffer - int iTo = 0; - for (int iFrom = _slideBufStartPos; iFrom < _slideBufFreePos; iFrom++, iTo++) - { - _slideBuffer[iTo] = _slideBuffer[iFrom]; - } - _slideBufFreePos -= _slideBufStartPos; // Note the -= - _slideBufStartPos = 0; - } - int obtained = StreamUtils.ReadRequestedBytes(_stream, _slideBuffer, _slideBufFreePos, lengthToRead); - _slideBufFreePos += obtained; - - // Recalculate how much data we now have - byteCount = _slideBufFreePos - _slideBufStartPos; - if (byteCount >= BLOCK_AND_AUTH) - { - var read = TransformAndBufferBlock(buffer, offset, bytesLeftToRead, CRYPTO_BLOCK_SIZE); - nBytes += read; - offset += read; - } - else - { - // Last round. - if (byteCount > AUTH_CODE_LENGTH) - { - // At least one byte of data plus auth code - int finalBlock = byteCount - AUTH_CODE_LENGTH; - nBytes += TransformAndBufferBlock(buffer, offset, bytesLeftToRead, finalBlock); - } - else if (byteCount < AUTH_CODE_LENGTH) - throw new ZipException("Internal error missed auth code"); // Coding bug - // Final block done. Check Auth code. - byte[] calcAuthCode = _transform.GetAuthCode(); - for (int i = 0; i < AUTH_CODE_LENGTH; i++) - { - if (calcAuthCode[i] != _slideBuffer[_slideBufStartPos + i]) - { - throw new ZipException("AES Authentication Code does not match. This is a super-CRC check on the data in the file after compression and encryption. \r\n" - + "The file may be damaged."); - } - } - - // don't need this any more, so use it as a 'complete' flag - _slideBuffer = null; - - break; // Reached the auth code - } - } - return nBytes; - } - - // read some buffered data - private int ReadBufferedData(byte[] buffer, int offset, int count) - { - int copyCount = Math.Min(count, _transformBufferFreePos - _transformBufferStartPos); - - Array.Copy(_transformBuffer, _transformBufferStartPos, buffer, offset, copyCount); - _transformBufferStartPos += copyCount; - - return copyCount; - } - - // Perform the crypto transform, and buffer the data if less than one block has been requested. - private int TransformAndBufferBlock(byte[] buffer, int offset, int count, int blockSize) - { - // If the requested data is greater than one block, transform it directly into the output - // If it's smaller, do it into a temporary buffer and copy the requested part - bool bufferRequired = (blockSize > count); - - if (bufferRequired && _transformBuffer == null) - _transformBuffer = new byte[CRYPTO_BLOCK_SIZE]; - - var targetBuffer = bufferRequired ? _transformBuffer : buffer; - var targetOffset = bufferRequired ? 0 : offset; - - // Transform the data - _transform.TransformBlock(_slideBuffer, - _slideBufStartPos, - blockSize, - targetBuffer, - targetOffset); - - _slideBufStartPos += blockSize; - - if (!bufferRequired) - { - return blockSize; - } - else - { - Array.Copy(_transformBuffer, 0, buffer, offset, count); - _transformBufferStartPos = count; - _transformBufferFreePos = blockSize; - - return count; - } - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The byte offset in buffer at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - public override void Write(byte[] buffer, int offset, int count) - { - // ZipAESStream is used for reading but not for writing. Writing uses the ZipAESTransform directly. - throw new NotImplementedException(); - } - } -} diff --git a/ICSharpCode.SharpZipLib/Encryption/ZipAESTransform.cs b/ICSharpCode.SharpZipLib/Encryption/ZipAESTransform.cs deleted file mode 100644 index 5aced2d71901..000000000000 --- a/ICSharpCode.SharpZipLib/Encryption/ZipAESTransform.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System; -using System.Security.Cryptography; -using ICSharpCode.SharpZipLib.Core; - -namespace ICSharpCode.SharpZipLib.Encryption -{ - /// - /// Transforms stream using AES in CTR mode - /// - internal class ZipAESTransform : ICryptoTransform - { -#if NET45 - class IncrementalHash : HMACSHA1 - { - bool _finalised; - public IncrementalHash(byte[] key) : base(key) { } - public static IncrementalHash CreateHMAC(string n, byte[] key) => new IncrementalHash(key); - public void AppendData(byte[] buffer, int offset, int count) => TransformBlock(buffer, offset, count, buffer, offset); - public byte[] GetHashAndReset() - { - if (!_finalised) - { - byte[] dummy = new byte[0]; - TransformFinalBlock(dummy, 0, 0); - _finalised = true; - } - return Hash; - } - } - - static class HashAlgorithmName - { - public static string SHA1 = null; - } -#endif - - private const int PWD_VER_LENGTH = 2; - - // WinZip use iteration count of 1000 for PBKDF2 key generation - private const int KEY_ROUNDS = 1000; - - // For 128-bit AES (16 bytes) the encryption is implemented as expected. - // For 256-bit AES (32 bytes) WinZip do full 256 bit AES of the nonce to create the encryption - // block but use only the first 16 bytes of it, and discard the second half. - private const int ENCRYPT_BLOCK = 16; - - private int _blockSize; - private readonly ICryptoTransform _encryptor; - private readonly byte[] _counterNonce; - private byte[] _encryptBuffer; - private int _encrPos; - private byte[] _pwdVerifier; - private IncrementalHash _hmacsha1; - private byte[] _authCode = null; - - private bool _writeMode; - - /// - /// Constructor. - /// - /// Password string - /// Random bytes, length depends on encryption strength. - /// 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes. - /// The encryption strength, in bytes eg 16 for 128 bits. - /// True when creating a zip, false when reading. For the AuthCode. - /// - public ZipAESTransform(string key, byte[] saltBytes, int blockSize, bool writeMode) - { - if (blockSize != 16 && blockSize != 32) // 24 valid for AES but not supported by Winzip - throw new Exception("Invalid blocksize " + blockSize + ". Must be 16 or 32."); - if (saltBytes.Length != blockSize / 2) - throw new Exception("Invalid salt len. Must be " + blockSize / 2 + " for blocksize " + blockSize); - // initialise the encryption buffer and buffer pos - _blockSize = blockSize; - _encryptBuffer = new byte[_blockSize]; - _encrPos = ENCRYPT_BLOCK; - - // Performs the equivalent of derive_key in Dr Brian Gladman's pwd2key.c - var pdb = new Rfc2898DeriveBytes(key, saltBytes, KEY_ROUNDS); - var rm = Aes.Create(); - rm.Mode = CipherMode.ECB; // No feedback from cipher for CTR mode - _counterNonce = new byte[_blockSize]; - byte[] key1bytes = pdb.GetBytes(_blockSize); - byte[] key2bytes = pdb.GetBytes(_blockSize); - - // Use empty IV for AES - _encryptor = rm.CreateEncryptor(key1bytes, new byte[16]); - _pwdVerifier = pdb.GetBytes(PWD_VER_LENGTH); - // - _hmacsha1 = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA1, key2bytes); - _writeMode = writeMode; - } - - /// - /// Implement the ICryptoTransform method. - /// - public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - // Pass the data stream to the hash algorithm for generating the Auth Code. - // This does not change the inputBuffer. Do this before decryption for read mode. - if (!_writeMode) - { - _hmacsha1.AppendData(inputBuffer, inputOffset, inputCount); - } - // Encrypt with AES in CTR mode. Regards to Dr Brian Gladman for this. - int ix = 0; - while (ix < inputCount) - { - if (_encrPos == ENCRYPT_BLOCK) - { - /* increment encryption nonce */ - int j = 0; - while (++_counterNonce[j] == 0) - { - ++j; - } - /* encrypt the nonce to form next xor buffer */ - _encryptor.TransformBlock(_counterNonce, 0, _blockSize, _encryptBuffer, 0); - _encrPos = 0; - } - outputBuffer[ix + outputOffset] = (byte)(inputBuffer[ix + inputOffset] ^ _encryptBuffer[_encrPos++]); - // - ix++; - } - if (_writeMode) - { - // This does not change the buffer. - _hmacsha1.AppendData(outputBuffer, outputOffset, inputCount); - } - return inputCount; - } - - /// - /// Returns the 2 byte password verifier - /// - public byte[] PwdVerifier - { - get - { - return _pwdVerifier; - } - } - - /// - /// Returns the 10 byte AUTH CODE to be checked or appended immediately following the AES data stream. - /// - public byte[] GetAuthCode() - { - if (_authCode == null) - { - _authCode = _hmacsha1.GetHashAndReset(); - } - return _authCode; - } - - #region ICryptoTransform Members - - /// - /// Not implemented. - /// - public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) - { - if(inputCount > 0) - { - throw new NotImplementedException("TransformFinalBlock is not implemented and inputCount is greater than 0"); - } - return Empty.Array(); - } - - /// - /// Gets the size of the input data blocks in bytes. - /// - public int InputBlockSize - { - get - { - return _blockSize; - } - } - - /// - /// Gets the size of the output data blocks in bytes. - /// - public int OutputBlockSize - { - get - { - return _blockSize; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - public bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - public bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Cleanup internal state. - /// - public void Dispose() - { - _encryptor.Dispose(); - } - - #endregion ICryptoTransform Members - } -} diff --git a/ICSharpCode.SharpZipLib/GZip/GZip.cs b/ICSharpCode.SharpZipLib/GZip/GZip.cs deleted file mode 100644 index e7e4763daea8..000000000000 --- a/ICSharpCode.SharpZipLib/GZip/GZip.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.IO; - -namespace ICSharpCode.SharpZipLib.GZip -{ - using static Zip.Compression.Deflater; - - /// - /// An example class to demonstrate compression and decompression of GZip streams. - /// - public static class GZip - { - /// - /// Decompress the input writing - /// uncompressed data to the output stream - /// - /// The readable stream containing data to decompress. - /// The output stream to receive the decompressed data. - /// Both streams are closed on completion if true. - /// Input or output stream is null - public static void Decompress(Stream inStream, Stream outStream, bool isStreamOwner) - { - if (inStream == null) - throw new ArgumentNullException(nameof(inStream), "Input stream is null"); - - if (outStream == null) - throw new ArgumentNullException(nameof(outStream), "Output stream is null"); - - try - { - using (GZipInputStream gzipInput = new GZipInputStream(inStream)) - { - gzipInput.IsStreamOwner = isStreamOwner; - Core.StreamUtils.Copy(gzipInput, outStream, new byte[4096]); - } - } - finally - { - if (isStreamOwner) - { - // inStream is closed by the GZipInputStream if stream owner - outStream.Dispose(); - } - } - } - - /// - /// Compress the input stream sending - /// result data to output stream - /// - /// The readable stream to compress. - /// The output stream to receive the compressed data. - /// Both streams are closed on completion if true. - /// Deflate buffer size, minimum 512 - /// Deflate compression level, 0-9 - /// Input or output stream is null - /// Buffer Size is smaller than 512 - /// Compression level outside 0-9 - public static void Compress(Stream inStream, Stream outStream, bool isStreamOwner, int bufferSize = 512, int level = 6) - { - if (inStream == null) - throw new ArgumentNullException(nameof(inStream), "Input stream is null"); - - if (outStream == null) - throw new ArgumentNullException(nameof(outStream), "Output stream is null"); - - if (bufferSize < 512) - throw new ArgumentOutOfRangeException(nameof(bufferSize), "Deflate buffer size must be >= 512"); - - if (level < NO_COMPRESSION || level > BEST_COMPRESSION) - throw new ArgumentOutOfRangeException(nameof(level), "Compression level must be 0-9"); - - try - { - using (GZipOutputStream gzipOutput = new GZipOutputStream(outStream, bufferSize)) - { - gzipOutput.SetLevel(level); - gzipOutput.IsStreamOwner = isStreamOwner; - Core.StreamUtils.Copy(inStream, gzipOutput, new byte[bufferSize]); - } - } - finally - { - if (isStreamOwner) - { - // outStream is closed by the GZipOutputStream if stream owner - inStream.Dispose(); - } - } - } - } -} diff --git a/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs b/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs deleted file mode 100644 index a59799278a99..000000000000 --- a/ICSharpCode.SharpZipLib/GZip/GZipConstants.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Text; - -namespace ICSharpCode.SharpZipLib.GZip -{ - /// - /// This class contains constants used for gzip. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "kept for backwards compatibility")] - sealed public class GZipConstants - { - /// - /// First GZip identification byte - /// - public const byte ID1 = 0x1F; - - /// - /// Second GZip identification byte - /// - public const byte ID2 = 0x8B; - - /// - /// Deflate compression method - /// - public const byte CompressionMethodDeflate = 0x8; - - /// - /// Get the GZip specified encoding (CP-1252 if supported, otherwise ASCII) - /// - public static Encoding Encoding - { - get - { - try - { - return Encoding.GetEncoding(1252); - } - catch - { - return Encoding.ASCII; - } - } - } - - } - - /// - /// GZip header flags - /// - [Flags] - public enum GZipFlags: byte - { - /// - /// Text flag hinting that the file is in ASCII - /// - FTEXT = 0x1 << 0, - - /// - /// CRC flag indicating that a CRC16 preceeds the data - /// - FHCRC = 0x1 << 1, - - /// - /// Extra flag indicating that extra fields are present - /// - FEXTRA = 0x1 << 2, - - /// - /// Filename flag indicating that the original filename is present - /// - FNAME = 0x1 << 3, - - /// - /// Flag bit mask indicating that a comment is present - /// - FCOMMENT = 0x1 << 4, - } -} diff --git a/ICSharpCode.SharpZipLib/GZip/GZipException.cs b/ICSharpCode.SharpZipLib/GZip/GZipException.cs deleted file mode 100644 index a0ec6bb51c7a..000000000000 --- a/ICSharpCode.SharpZipLib/GZip/GZipException.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib.GZip -{ - /// - /// GZipException represents exceptions specific to GZip classes and code. - /// - [Serializable] - public class GZipException : SharpZipBaseException - { - /// - /// Initialise a new instance of . - /// - public GZipException() - { - } - - /// - /// Initialise a new instance of with its message string. - /// - /// A that describes the error. - public GZipException(string message) - : base(message) - { - } - - /// - /// Initialise a new instance of . - /// - /// A that describes the error. - /// The that caused this exception. - public GZipException(string message, Exception innerException) - : base(message, innerException) - { - } - - /// - /// Initializes a new instance of the GZipException class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected GZipException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs b/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs deleted file mode 100644 index 20a4ded17f76..000000000000 --- a/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs +++ /dev/null @@ -1,361 +0,0 @@ -using ICSharpCode.SharpZipLib.Checksum; -using ICSharpCode.SharpZipLib.Zip.Compression; -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; -using System; -using System.IO; -using System.Text; - -namespace ICSharpCode.SharpZipLib.GZip -{ - /// - /// This filter stream is used to decompress a "GZIP" format stream. - /// The "GZIP" format is described baseInputStream RFC 1952. - /// - /// author of the original java version : John Leuner - /// - /// This sample shows how to unzip a gzipped file - /// - /// using System; - /// using System.IO; - /// - /// using ICSharpCode.SharpZipLib.Core; - /// using ICSharpCode.SharpZipLib.GZip; - /// - /// class MainClass - /// { - /// public static void Main(string[] args) - /// { - /// using (Stream inStream = new GZipInputStream(File.OpenRead(args[0]))) - /// using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) { - /// byte[] buffer = new byte[4096]; - /// StreamUtils.Copy(inStream, outStream, buffer); - /// } - /// } - /// } - /// - /// - public class GZipInputStream : InflaterInputStream - { - #region Instance Fields - - /// - /// CRC-32 value for uncompressed data - /// - protected Crc32 crc; - - /// - /// Flag to indicate if we've read the GZIP header yet for the current member (block of compressed data). - /// This is tracked per-block as the file is parsed. - /// - private bool readGZIPHeader; - - /// - /// Flag to indicate if at least one block in a stream with concatenated blocks was read successfully. - /// This allows us to exit gracefully if downstream data is not in gzip format. - /// - private bool completedLastBlock; - - private string fileName; - - #endregion Instance Fields - - #region Constructors - - /// - /// Creates a GZipInputStream with the default buffer size - /// - /// - /// The stream to read compressed data from (baseInputStream GZIP format) - /// - public GZipInputStream(Stream baseInputStream) - : this(baseInputStream, 4096) - { - } - - /// - /// Creates a GZIPInputStream with the specified buffer size - /// - /// - /// The stream to read compressed data from (baseInputStream GZIP format) - /// - /// - /// Size of the buffer to use - /// - public GZipInputStream(Stream baseInputStream, int size) - : base(baseInputStream, new Inflater(true), size) - { - } - - #endregion Constructors - - #region Stream overrides - - /// - /// Reads uncompressed data into an array of bytes - /// - /// - /// The buffer to read uncompressed data into - /// - /// - /// The offset indicating where the data should be placed - /// - /// - /// The number of uncompressed bytes to be read - /// - /// Returns the number of bytes actually read. - public override int Read(byte[] buffer, int offset, int count) - { - // A GZIP file can contain multiple blocks of compressed data, although this is quite rare. - // A compressed block could potentially be empty, so we need to loop until we reach EOF or - // we find data. - while (true) - { - // If we haven't read the header for this block, read it - if (!readGZIPHeader) - { - // Try to read header. If there is no header (0 bytes available), this is EOF. If there is - // an incomplete header, this will throw an exception. - try - { - if (!ReadHeader()) - { - return 0; - } - } - catch (Exception ex) when (completedLastBlock && (ex is GZipException || ex is EndOfStreamException)) - { - // if we completed the last block (i.e. we're in a stream that has multiple blocks concatenated - // we want to return gracefully from any header parsing exceptions since sometimes there may - // be trailing garbage on a stream - return 0; - } - } - - // Try to read compressed data - int bytesRead = base.Read(buffer, offset, count); - if (bytesRead > 0) - { - crc.Update(new ArraySegment(buffer, offset, bytesRead)); - } - - // If this is the end of stream, read the footer - if (inf.IsFinished) - { - ReadFooter(); - } - - // Attempting to read 0 bytes will never yield any bytesRead, so we return instead of looping forever - if (bytesRead > 0 || count == 0) - { - return bytesRead; - } - } - } - - /// - /// Retrieves the filename header field for the block last read - /// - /// - public string GetFilename() - { - return fileName; - } - - #endregion Stream overrides - - #region Support routines - - private bool ReadHeader() - { - // Initialize CRC for this block - crc = new Crc32(); - - // Make sure there is data in file. We can't rely on ReadLeByte() to fill the buffer, as this could be EOF, - // which is fine, but ReadLeByte() throws an exception if it doesn't find data, so we do this part ourselves. - if (inputBuffer.Available <= 0) - { - inputBuffer.Fill(); - if (inputBuffer.Available <= 0) - { - // No header, EOF. - return false; - } - } - - var headCRC = new Crc32(); - - // 1. Check the two magic bytes - - var magic = inputBuffer.ReadLeByte(); - headCRC.Update(magic); - if (magic != GZipConstants.ID1) - { - throw new GZipException("Error GZIP header, first magic byte doesn't match"); - } - - magic = inputBuffer.ReadLeByte(); - if (magic != GZipConstants.ID2) - { - throw new GZipException("Error GZIP header, second magic byte doesn't match"); - } - headCRC.Update(magic); - - // 2. Check the compression type (must be 8) - var compressionType = inputBuffer.ReadLeByte(); - - if (compressionType != GZipConstants.CompressionMethodDeflate) - { - throw new GZipException("Error GZIP header, data not in deflate format"); - } - headCRC.Update(compressionType); - - // 3. Check the flags - var flagsByte = inputBuffer.ReadLeByte(); - - headCRC.Update(flagsByte); - - // 3.1 Check the reserved bits are zero - - if ((flagsByte & 0xE0) != 0) - { - throw new GZipException("Reserved flag bits in GZIP header != 0"); - } - - var flags = (GZipFlags)flagsByte; - - // 4.-6. Skip the modification time, extra flags, and OS type - for (int i = 0; i < 6; i++) - { - headCRC.Update(inputBuffer.ReadLeByte()); - } - - // 7. Read extra field - if (flags.HasFlag(GZipFlags.FEXTRA)) - { - // XLEN is total length of extra subfields, we will skip them all - var len1 = inputBuffer.ReadLeByte(); - var len2 = inputBuffer.ReadLeByte(); - - headCRC.Update(len1); - headCRC.Update(len2); - - int extraLen = (len2 << 8) | len1; // gzip is LSB first - for (int i = 0; i < extraLen; i++) - { - headCRC.Update(inputBuffer.ReadLeByte()); - } - } - - // 8. Read file name - if (flags.HasFlag(GZipFlags.FNAME)) - { - var fname = new byte[1024]; - var fnamePos = 0; - int readByte; - while ((readByte = inputBuffer.ReadLeByte()) > 0) - { - if (fnamePos < 1024) - { - fname[fnamePos++] = (byte)readByte; - } - headCRC.Update(readByte); - } - - headCRC.Update(readByte); - - fileName = GZipConstants.Encoding.GetString(fname, 0, fnamePos); - } - else - { - fileName = null; - } - - // 9. Read comment - if (flags.HasFlag(GZipFlags.FCOMMENT)) - { - int readByte; - while ((readByte = inputBuffer.ReadLeByte()) > 0) - { - headCRC.Update(readByte); - } - - headCRC.Update(readByte); - } - - // 10. Read header CRC - if (flags.HasFlag(GZipFlags.FHCRC)) - { - int tempByte; - int crcval = inputBuffer.ReadLeByte(); - if (crcval < 0) - { - throw new EndOfStreamException("EOS reading GZIP header"); - } - - tempByte = inputBuffer.ReadLeByte(); - if (tempByte < 0) - { - throw new EndOfStreamException("EOS reading GZIP header"); - } - - crcval = (crcval << 8) | tempByte; - if (crcval != ((int)headCRC.Value & 0xffff)) - { - throw new GZipException("Header CRC value mismatch"); - } - } - - readGZIPHeader = true; - return true; - } - - private void ReadFooter() - { - byte[] footer = new byte[8]; - - // End of stream; reclaim all bytes from inf, read the final byte count, and reset the inflator - long bytesRead = inf.TotalOut & 0xffffffff; - inputBuffer.Available += inf.RemainingInput; - inf.Reset(); - - // Read footer from inputBuffer - int needed = 8; - while (needed > 0) - { - int count = inputBuffer.ReadClearTextBuffer(footer, 8 - needed, needed); - if (count <= 0) - { - throw new EndOfStreamException("EOS reading GZIP footer"); - } - needed -= count; // Jewel Jan 16 - } - - // Calculate CRC - int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) | ((footer[2] & 0xff) << 16) | (footer[3] << 24); - if (crcval != (int)crc.Value) - { - throw new GZipException("GZIP crc sum mismatch, theirs \"" + crcval + "\" and ours \"" + (int)crc.Value); - } - - // NOTE The total here is the original total modulo 2 ^ 32. - uint total = - (uint)((uint)footer[4] & 0xff) | - (uint)(((uint)footer[5] & 0xff) << 8) | - (uint)(((uint)footer[6] & 0xff) << 16) | - (uint)((uint)footer[7] << 24); - - if (bytesRead != total) - { - throw new GZipException("Number of bytes mismatch in footer"); - } - - // Mark header read as false so if another header exists, we'll continue reading through the file - readGZIPHeader = false; - - // Indicate that we succeeded on at least one block so we can exit gracefully if there is trailing garbage downstream - completedLastBlock = true; - } - - #endregion Support routines - } -} diff --git a/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs b/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs deleted file mode 100644 index 0b1a647fea53..000000000000 --- a/ICSharpCode.SharpZipLib/GZip/GzipOutputStream.cs +++ /dev/null @@ -1,293 +0,0 @@ -using ICSharpCode.SharpZipLib.Checksum; -using ICSharpCode.SharpZipLib.Zip.Compression; -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; -using System; -using System.IO; -using System.Text; - -namespace ICSharpCode.SharpZipLib.GZip -{ - /// - /// This filter stream is used to compress a stream into a "GZIP" stream. - /// The "GZIP" format is described in RFC 1952. - /// - /// author of the original java version : John Leuner - /// - /// This sample shows how to gzip a file - /// - /// using System; - /// using System.IO; - /// - /// using ICSharpCode.SharpZipLib.GZip; - /// using ICSharpCode.SharpZipLib.Core; - /// - /// class MainClass - /// { - /// public static void Main(string[] args) - /// { - /// using (Stream s = new GZipOutputStream(File.Create(args[0] + ".gz"))) - /// using (FileStream fs = File.OpenRead(args[0])) { - /// byte[] writeData = new byte[4096]; - /// Streamutils.Copy(s, fs, writeData); - /// } - /// } - /// } - /// } - /// - /// - public class GZipOutputStream : DeflaterOutputStream - { - private enum OutputState - { - Header, - Footer, - Finished, - Closed, - }; - - #region Instance Fields - - /// - /// CRC-32 value for uncompressed data - /// - protected Crc32 crc = new Crc32(); - - private OutputState state_ = OutputState.Header; - - private string fileName; - - private GZipFlags flags = 0; - - #endregion Instance Fields - - #region Constructors - - /// - /// Creates a GzipOutputStream with the default buffer size - /// - /// - /// The stream to read data (to be compressed) from - /// - public GZipOutputStream(Stream baseOutputStream) - : this(baseOutputStream, 4096) - { - } - - /// - /// Creates a GZipOutputStream with the specified buffer size - /// - /// - /// The stream to read data (to be compressed) from - /// - /// - /// Size of the buffer to use - /// - public GZipOutputStream(Stream baseOutputStream, int size) : base(baseOutputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size) - { - } - - #endregion Constructors - - #region Public API - - /// - /// Sets the active compression level (0-9). The new level will be activated - /// immediately. - /// - /// The compression level to set. - /// - /// Level specified is not supported. - /// - /// - public void SetLevel(int level) - { - if (level < Deflater.NO_COMPRESSION || level > Deflater.BEST_COMPRESSION) - throw new ArgumentOutOfRangeException(nameof(level), "Compression level must be 0-9"); - - deflater_.SetLevel(level); - } - - /// - /// Get the current compression level. - /// - /// The current compression level. - public int GetLevel() - { - return deflater_.GetLevel(); - } - - /// - /// Original filename - /// - public string FileName - { - get => fileName; - set - { - fileName = CleanFilename(value); - if (string.IsNullOrEmpty(fileName)) - { - flags &= ~GZipFlags.FNAME; - } - else - { - flags |= GZipFlags.FNAME; - } - } - } - - #endregion Public API - - #region Stream overrides - - /// - /// Write given buffer to output updating crc - /// - /// Buffer to write - /// Offset of first byte in buf to write - /// Number of bytes to write - public override void Write(byte[] buffer, int offset, int count) - { - if (state_ == OutputState.Header) - { - WriteHeader(); - } - - if (state_ != OutputState.Footer) - { - throw new InvalidOperationException("Write not permitted in current state"); - } - - crc.Update(new ArraySegment(buffer, offset, count)); - base.Write(buffer, offset, count); - } - - /// - /// Writes remaining compressed output data to the output stream - /// and closes it. - /// - protected override void Dispose(bool disposing) - { - try - { - Finish(); - } - finally - { - if (state_ != OutputState.Closed) - { - state_ = OutputState.Closed; - if (IsStreamOwner) - { - baseOutputStream_.Dispose(); - } - } - } - } - - /// - /// Flushes the stream by ensuring the header is written, and then calling Flush - /// on the deflater. - /// - public override void Flush() - { - if (state_ == OutputState.Header) - { - WriteHeader(); - } - - base.Flush(); - } - - #endregion Stream overrides - - #region DeflaterOutputStream overrides - - /// - /// Finish compression and write any footer information required to stream - /// - public override void Finish() - { - // If no data has been written a header should be added. - if (state_ == OutputState.Header) - { - WriteHeader(); - } - - if (state_ == OutputState.Footer) - { - state_ = OutputState.Finished; - base.Finish(); - - var totalin = (uint)(deflater_.TotalIn & 0xffffffff); - var crcval = (uint)(crc.Value & 0xffffffff); - - byte[] gzipFooter; - - unchecked - { - gzipFooter = new byte[] { - (byte) crcval, (byte) (crcval >> 8), - (byte) (crcval >> 16), (byte) (crcval >> 24), - - (byte) totalin, (byte) (totalin >> 8), - (byte) (totalin >> 16), (byte) (totalin >> 24) - }; - } - - baseOutputStream_.Write(gzipFooter, 0, gzipFooter.Length); - } - } - - #endregion DeflaterOutputStream overrides - - #region Support Routines - - private static string CleanFilename(string path) - => path.Substring(path.LastIndexOf('/') + 1); - - private void WriteHeader() - { - if (state_ == OutputState.Header) - { - state_ = OutputState.Footer; - - var mod_time = (int)((DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000L); // Ticks give back 100ns intervals - byte[] gzipHeader = { - // The two magic bytes - GZipConstants.ID1, - GZipConstants.ID2, - - // The compression type - GZipConstants.CompressionMethodDeflate, - - // The flags (not set) - (byte)flags, - - // The modification time - (byte) mod_time, (byte) (mod_time >> 8), - (byte) (mod_time >> 16), (byte) (mod_time >> 24), - - // The extra flags - 0, - - // The OS type (unknown) - 255 - }; - - baseOutputStream_.Write(gzipHeader, 0, gzipHeader.Length); - - if (flags.HasFlag(GZipFlags.FNAME)) - { - var fname = GZipConstants.Encoding.GetBytes(fileName); - baseOutputStream_.Write(fname, 0, fname.Length); - - // End filename string with a \0 - baseOutputStream_.Write(new byte[] { 0 }, 0, 1); - } - } - } - - #endregion Support Routines - } -} diff --git a/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj b/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj deleted file mode 100644 index 7e2adffd5971..000000000000 --- a/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj +++ /dev/null @@ -1,44 +0,0 @@ - - - - netstandard2.0;netstandard2.1;net45 - True - assets/ICSharpCode.SharpZipLib.snk - true - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - - - - - 1.3.3 - $(Version).11 - $(FileVersion) - SharpZipLib - ICSharpCode - ICSharpCode - SharpZipLib (#ziplib, formerly NZipLib) is a compression library for Zip, GZip, BZip2, and Tar written entirely in C# for .NET. It is implemented as an assembly (installable in the GAC), and thus can easily be incorporated into other projects (in any .NET language) - MIT - http://icsharpcode.github.io/SharpZipLib/ - images/sharpziplib-nuget-256x256.png - https://github.com/icsharpcode/SharpZipLib - Copyright © 2000-2021 SharpZipLib Contributors - Compression Library Zip GZip BZip2 LZW Tar - en-US - -Please see https://github.com/icsharpcode/SharpZipLib/wiki/Release-1.3.3 for more information. - https://github.com/icsharpcode/SharpZipLib - - - - - - - - - True - images - - - - diff --git a/ICSharpCode.SharpZipLib/Lzw/LzwConstants.cs b/ICSharpCode.SharpZipLib/Lzw/LzwConstants.cs deleted file mode 100644 index 88934830fda3..000000000000 --- a/ICSharpCode.SharpZipLib/Lzw/LzwConstants.cs +++ /dev/null @@ -1,63 +0,0 @@ -namespace ICSharpCode.SharpZipLib.Lzw -{ - /// - /// This class contains constants used for LZW - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "kept for backwards compatibility")] - sealed public class LzwConstants - { - /// - /// Magic number found at start of LZW header: 0x1f 0x9d - /// - public const int MAGIC = 0x1f9d; - - /// - /// Maximum number of bits per code - /// - public const int MAX_BITS = 16; - - /* 3rd header byte: - * bit 0..4 Number of compression bits - * bit 5 Extended header - * bit 6 Free - * bit 7 Block mode - */ - - /// - /// Mask for 'number of compression bits' - /// - public const int BIT_MASK = 0x1f; - - /// - /// Indicates the presence of a fourth header byte - /// - public const int EXTENDED_MASK = 0x20; - - //public const int FREE_MASK = 0x40; - - /// - /// Reserved bits - /// - public const int RESERVED_MASK = 0x60; - - /// - /// Block compression: if table is full and compression rate is dropping, - /// clear the dictionary. - /// - public const int BLOCK_MODE_MASK = 0x80; - - /// - /// LZW file header size (in bytes) - /// - public const int HDR_SIZE = 3; - - /// - /// Initial number of bits per code - /// - public const int INIT_BITS = 9; - - private LzwConstants() - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Lzw/LzwException.cs b/ICSharpCode.SharpZipLib/Lzw/LzwException.cs deleted file mode 100644 index 1d5c44c3c9b1..000000000000 --- a/ICSharpCode.SharpZipLib/Lzw/LzwException.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib.Lzw -{ - /// - /// LzwException represents exceptions specific to LZW classes and code. - /// - [Serializable] - public class LzwException : SharpZipBaseException - { - /// - /// Initialise a new instance of . - /// - public LzwException() - { - } - - /// - /// Initialise a new instance of with its message string. - /// - /// A that describes the error. - public LzwException(string message) - : base(message) - { - } - - /// - /// Initialise a new instance of . - /// - /// A that describes the error. - /// The that caused this exception. - public LzwException(string message, Exception innerException) - : base(message, innerException) - { - } - - /// - /// Initializes a new instance of the LzwException class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected LzwException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Lzw/LzwInputStream.cs b/ICSharpCode.SharpZipLib/Lzw/LzwInputStream.cs deleted file mode 100644 index 1045ef77875f..000000000000 --- a/ICSharpCode.SharpZipLib/Lzw/LzwInputStream.cs +++ /dev/null @@ -1,572 +0,0 @@ -using System; -using System.IO; - -namespace ICSharpCode.SharpZipLib.Lzw -{ - /// - /// This filter stream is used to decompress a LZW format stream. - /// Specifically, a stream that uses the LZC compression method. - /// This file format is usually associated with the .Z file extension. - /// - /// See http://en.wikipedia.org/wiki/Compress - /// See http://wiki.wxwidgets.org/Development:_Z_File_Format - /// - /// The file header consists of 3 (or optionally 4) bytes. The first two bytes - /// contain the magic marker "0x1f 0x9d", followed by a byte of flags. - /// - /// Based on Java code by Ronald Tschalar, which in turn was based on the unlzw.c - /// code in the gzip package. - /// - /// This sample shows how to unzip a compressed file - /// - /// using System; - /// using System.IO; - /// - /// using ICSharpCode.SharpZipLib.Core; - /// using ICSharpCode.SharpZipLib.LZW; - /// - /// class MainClass - /// { - /// public static void Main(string[] args) - /// { - /// using (Stream inStream = new LzwInputStream(File.OpenRead(args[0]))) - /// using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) { - /// byte[] buffer = new byte[4096]; - /// StreamUtils.Copy(inStream, outStream, buffer); - /// // OR - /// inStream.Read(buffer, 0, buffer.Length); - /// // now do something with the buffer - /// } - /// } - /// } - /// - /// - public class LzwInputStream : Stream - { - /// - /// Gets or sets a flag indicating ownership of underlying stream. - /// When the flag is true will close the underlying stream also. - /// - /// The default value is true. - public bool IsStreamOwner { get; set; } = true; - - /// - /// Creates a LzwInputStream - /// - /// - /// The stream to read compressed data from (baseInputStream LZW format) - /// - public LzwInputStream(Stream baseInputStream) - { - this.baseInputStream = baseInputStream; - } - - /// - /// See - /// - /// - public override int ReadByte() - { - int b = Read(one, 0, 1); - if (b == 1) - return (one[0] & 0xff); - return -1; - } - - /// - /// Reads decompressed data into the provided buffer byte array - /// - /// - /// The array to read and decompress data into - /// - /// - /// The offset indicating where the data should be placed - /// - /// - /// The number of bytes to decompress - /// - /// The number of bytes read. Zero signals the end of stream - public override int Read(byte[] buffer, int offset, int count) - { - if (!headerParsed) - ParseHeader(); - - if (eof) - return 0; - - int start = offset; - - /* Using local copies of various variables speeds things up by as - * much as 30% in Java! Performance not tested in C#. - */ - int[] lTabPrefix = tabPrefix; - byte[] lTabSuffix = tabSuffix; - byte[] lStack = stack; - int lNBits = nBits; - int lMaxCode = maxCode; - int lMaxMaxCode = maxMaxCode; - int lBitMask = bitMask; - int lOldCode = oldCode; - byte lFinChar = finChar; - int lStackP = stackP; - int lFreeEnt = freeEnt; - byte[] lData = data; - int lBitPos = bitPos; - - // empty stack if stuff still left - int sSize = lStack.Length - lStackP; - if (sSize > 0) - { - int num = (sSize >= count) ? count : sSize; - Array.Copy(lStack, lStackP, buffer, offset, num); - offset += num; - count -= num; - lStackP += num; - } - - if (count == 0) - { - stackP = lStackP; - return offset - start; - } - - // loop, filling local buffer until enough data has been decompressed - MainLoop: - do - { - if (end < EXTRA) - { - Fill(); - } - - int bitIn = (got > 0) ? (end - end % lNBits) << 3 : - (end << 3) - (lNBits - 1); - - while (lBitPos < bitIn) - { - #region A - - // handle 1-byte reads correctly - if (count == 0) - { - nBits = lNBits; - maxCode = lMaxCode; - maxMaxCode = lMaxMaxCode; - bitMask = lBitMask; - oldCode = lOldCode; - finChar = lFinChar; - stackP = lStackP; - freeEnt = lFreeEnt; - bitPos = lBitPos; - - return offset - start; - } - - // check for code-width expansion - if (lFreeEnt > lMaxCode) - { - int nBytes = lNBits << 3; - lBitPos = (lBitPos - 1) + - nBytes - (lBitPos - 1 + nBytes) % nBytes; - - lNBits++; - lMaxCode = (lNBits == maxBits) ? lMaxMaxCode : - (1 << lNBits) - 1; - - lBitMask = (1 << lNBits) - 1; - lBitPos = ResetBuf(lBitPos); - goto MainLoop; - } - - #endregion A - - #region B - - // read next code - int pos = lBitPos >> 3; - int code = (((lData[pos] & 0xFF) | - ((lData[pos + 1] & 0xFF) << 8) | - ((lData[pos + 2] & 0xFF) << 16)) >> - (lBitPos & 0x7)) & lBitMask; - - lBitPos += lNBits; - - // handle first iteration - if (lOldCode == -1) - { - if (code >= 256) - throw new LzwException("corrupt input: " + code + " > 255"); - - lFinChar = (byte)(lOldCode = code); - buffer[offset++] = lFinChar; - count--; - continue; - } - - // handle CLEAR code - if (code == TBL_CLEAR && blockMode) - { - Array.Copy(zeros, 0, lTabPrefix, 0, zeros.Length); - lFreeEnt = TBL_FIRST - 1; - - int nBytes = lNBits << 3; - lBitPos = (lBitPos - 1) + nBytes - (lBitPos - 1 + nBytes) % nBytes; - lNBits = LzwConstants.INIT_BITS; - lMaxCode = (1 << lNBits) - 1; - lBitMask = lMaxCode; - - // Code tables reset - - lBitPos = ResetBuf(lBitPos); - goto MainLoop; - } - - #endregion B - - #region C - - // setup - int inCode = code; - lStackP = lStack.Length; - - // Handle KwK case - if (code >= lFreeEnt) - { - if (code > lFreeEnt) - { - throw new LzwException("corrupt input: code=" + code + - ", freeEnt=" + lFreeEnt); - } - - lStack[--lStackP] = lFinChar; - code = lOldCode; - } - - // Generate output characters in reverse order - while (code >= 256) - { - lStack[--lStackP] = lTabSuffix[code]; - code = lTabPrefix[code]; - } - - lFinChar = lTabSuffix[code]; - buffer[offset++] = lFinChar; - count--; - - // And put them out in forward order - sSize = lStack.Length - lStackP; - int num = (sSize >= count) ? count : sSize; - Array.Copy(lStack, lStackP, buffer, offset, num); - offset += num; - count -= num; - lStackP += num; - - #endregion C - - #region D - - // generate new entry in table - if (lFreeEnt < lMaxMaxCode) - { - lTabPrefix[lFreeEnt] = lOldCode; - lTabSuffix[lFreeEnt] = lFinChar; - lFreeEnt++; - } - - // Remember previous code - lOldCode = inCode; - - // if output buffer full, then return - if (count == 0) - { - nBits = lNBits; - maxCode = lMaxCode; - bitMask = lBitMask; - oldCode = lOldCode; - finChar = lFinChar; - stackP = lStackP; - freeEnt = lFreeEnt; - bitPos = lBitPos; - - return offset - start; - } - - #endregion D - } // while - - lBitPos = ResetBuf(lBitPos); - } while (got > 0); // do..while - - nBits = lNBits; - maxCode = lMaxCode; - bitMask = lBitMask; - oldCode = lOldCode; - finChar = lFinChar; - stackP = lStackP; - freeEnt = lFreeEnt; - bitPos = lBitPos; - - eof = true; - return offset - start; - } - - /// - /// Moves the unread data in the buffer to the beginning and resets - /// the pointers. - /// - /// - /// - private int ResetBuf(int bitPosition) - { - int pos = bitPosition >> 3; - Array.Copy(data, pos, data, 0, end - pos); - end -= pos; - return 0; - } - - private void Fill() - { - got = baseInputStream.Read(data, end, data.Length - 1 - end); - if (got > 0) - { - end += got; - } - } - - private void ParseHeader() - { - headerParsed = true; - - byte[] hdr = new byte[LzwConstants.HDR_SIZE]; - - int result = baseInputStream.Read(hdr, 0, hdr.Length); - - // Check the magic marker - if (result < 0) - throw new LzwException("Failed to read LZW header"); - - if (hdr[0] != (LzwConstants.MAGIC >> 8) || hdr[1] != (LzwConstants.MAGIC & 0xff)) - { - throw new LzwException(String.Format( - "Wrong LZW header. Magic bytes don't match. 0x{0:x2} 0x{1:x2}", - hdr[0], hdr[1])); - } - - // Check the 3rd header byte - blockMode = (hdr[2] & LzwConstants.BLOCK_MODE_MASK) > 0; - maxBits = hdr[2] & LzwConstants.BIT_MASK; - - if (maxBits > LzwConstants.MAX_BITS) - { - throw new LzwException("Stream compressed with " + maxBits + - " bits, but decompression can only handle " + - LzwConstants.MAX_BITS + " bits."); - } - - if ((hdr[2] & LzwConstants.RESERVED_MASK) > 0) - { - throw new LzwException("Unsupported bits set in the header."); - } - - // Initialize variables - maxMaxCode = 1 << maxBits; - nBits = LzwConstants.INIT_BITS; - maxCode = (1 << nBits) - 1; - bitMask = maxCode; - oldCode = -1; - finChar = 0; - freeEnt = blockMode ? TBL_FIRST : 256; - - tabPrefix = new int[1 << maxBits]; - tabSuffix = new byte[1 << maxBits]; - stack = new byte[1 << maxBits]; - stackP = stack.Length; - - for (int idx = 255; idx >= 0; idx--) - tabSuffix[idx] = (byte)idx; - } - - #region Stream Overrides - - /// - /// Gets a value indicating whether the current stream supports reading - /// - public override bool CanRead - { - get - { - return baseInputStream.CanRead; - } - } - - /// - /// Gets a value of false indicating seeking is not supported for this stream. - /// - public override bool CanSeek - { - get - { - return false; - } - } - - /// - /// Gets a value of false indicating that this stream is not writeable. - /// - public override bool CanWrite - { - get - { - return false; - } - } - - /// - /// A value representing the length of the stream in bytes. - /// - public override long Length - { - get - { - return got; - } - } - - /// - /// The current position within the stream. - /// Throws a NotSupportedException when attempting to set the position - /// - /// Attempting to set the position - public override long Position - { - get - { - return baseInputStream.Position; - } - set - { - throw new NotSupportedException("InflaterInputStream Position not supported"); - } - } - - /// - /// Flushes the baseInputStream - /// - public override void Flush() - { - baseInputStream.Flush(); - } - - /// - /// Sets the position within the current stream - /// Always throws a NotSupportedException - /// - /// The relative offset to seek to. - /// The defining where to seek from. - /// The new position in the stream. - /// Any access - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException("Seek not supported"); - } - - /// - /// Set the length of the current stream - /// Always throws a NotSupportedException - /// - /// The new length value for the stream. - /// Any access - public override void SetLength(long value) - { - throw new NotSupportedException("InflaterInputStream SetLength not supported"); - } - - /// - /// Writes a sequence of bytes to stream and advances the current position - /// This method always throws a NotSupportedException - /// - /// The buffer containing data to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// Any access - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException("InflaterInputStream Write not supported"); - } - - /// - /// Writes one byte to the current stream and advances the current position - /// Always throws a NotSupportedException - /// - /// The byte to write. - /// Any access - public override void WriteByte(byte value) - { - throw new NotSupportedException("InflaterInputStream WriteByte not supported"); - } - - /// - /// Closes the input stream. When - /// is true the underlying stream is also closed. - /// - protected override void Dispose(bool disposing) - { - if (!isClosed) - { - isClosed = true; - if (IsStreamOwner) - { - baseInputStream.Dispose(); - } - } - } - - #endregion Stream Overrides - - #region Instance Fields - - private Stream baseInputStream; - - /// - /// Flag indicating wether this instance has been closed or not. - /// - private bool isClosed; - - private readonly byte[] one = new byte[1]; - private bool headerParsed; - - // string table stuff - private const int TBL_CLEAR = 0x100; - - private const int TBL_FIRST = TBL_CLEAR + 1; - - private int[] tabPrefix; - private byte[] tabSuffix; - private readonly int[] zeros = new int[256]; - private byte[] stack; - - // various state - private bool blockMode; - - private int nBits; - private int maxBits; - private int maxMaxCode; - private int maxCode; - private int bitMask; - private int oldCode; - private byte finChar; - private int stackP; - private int freeEnt; - - // input buffer - private readonly byte[] data = new byte[1024 * 8]; - - private int bitPos; - private int end; - private int got; - private bool eof; - private const int EXTRA = 64; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Tar/InvalidHeaderException.cs b/ICSharpCode.SharpZipLib/Tar/InvalidHeaderException.cs deleted file mode 100644 index 9f385e425ab0..000000000000 --- a/ICSharpCode.SharpZipLib/Tar/InvalidHeaderException.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib.Tar -{ - /// - /// This exception is used to indicate that there is a problem - /// with a TAR archive header. - /// - [Serializable] - public class InvalidHeaderException : TarException - { - /// - /// Initialise a new instance of the InvalidHeaderException class. - /// - public InvalidHeaderException() - { - } - - /// - /// Initialises a new instance of the InvalidHeaderException class with a specified message. - /// - /// Message describing the exception cause. - public InvalidHeaderException(string message) - : base(message) - { - } - - /// - /// Initialise a new instance of InvalidHeaderException - /// - /// Message describing the problem. - /// The exception that is the cause of the current exception. - public InvalidHeaderException(string message, Exception exception) - : base(message, exception) - { - } - - /// - /// Initializes a new instance of the InvalidHeaderException class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected InvalidHeaderException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Tar/TarArchive.cs b/ICSharpCode.SharpZipLib/Tar/TarArchive.cs deleted file mode 100644 index 6db6b23b9a73..000000000000 --- a/ICSharpCode.SharpZipLib/Tar/TarArchive.cs +++ /dev/null @@ -1,1028 +0,0 @@ -using System; -using System.IO; -using System.Numerics; -using System.Text; -using ICSharpCode.SharpZipLib.Core; - -namespace ICSharpCode.SharpZipLib.Tar -{ - /// - /// Used to advise clients of 'events' while processing archives - /// - public delegate void ProgressMessageHandler(TarArchive archive, TarEntry entry, string message); - - /// - /// The TarArchive class implements the concept of a - /// 'Tape Archive'. A tar archive is a series of entries, each of - /// which represents a file system object. Each entry in - /// the archive consists of a header block followed by 0 or more data blocks. - /// Directory entries consist only of the header block, and are followed by entries - /// for the directory's contents. File entries consist of a - /// header followed by the number of blocks needed to - /// contain the file's contents. All entries are written on - /// block boundaries. Blocks are 512 bytes long. - /// - /// TarArchives are instantiated in either read or write mode, - /// based upon whether they are instantiated with an InputStream - /// or an OutputStream. Once instantiated TarArchives read/write - /// mode can not be changed. - /// - /// There is currently no support for random access to tar archives. - /// However, it seems that subclassing TarArchive, and using the - /// TarBuffer.CurrentRecord and TarBuffer.CurrentBlock - /// properties, this would be rather trivial. - /// - public class TarArchive : IDisposable - { - /// - /// Client hook allowing detailed information to be reported during processing - /// - public event ProgressMessageHandler ProgressMessageEvent; - - /// - /// Raises the ProgressMessage event - /// - /// The TarEntry for this event - /// message for this event. Null is no message - protected virtual void OnProgressMessageEvent(TarEntry entry, string message) - { - ProgressMessageHandler handler = ProgressMessageEvent; - if (handler != null) - { - handler(this, entry, message); - } - } - - #region Constructors - - /// - /// Constructor for a default . - /// - protected TarArchive() - { - } - - /// - /// Initialise a TarArchive for input. - /// - /// The to use for input. - protected TarArchive(TarInputStream stream) - { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - tarIn = stream; - } - - /// - /// Initialise a TarArchive for output. - /// - /// The to use for output. - protected TarArchive(TarOutputStream stream) - { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - tarOut = stream; - } - - #endregion Constructors - - #region Static factory methods - - /// - /// The InputStream based constructors create a TarArchive for the - /// purposes of extracting or listing a tar archive. Thus, use - /// these constructors when you wish to extract files from or list - /// the contents of an existing tar archive. - /// - /// The stream to retrieve archive data from. - /// Returns a new suitable for reading from. - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public static TarArchive CreateInputTarArchive(Stream inputStream) - { - return CreateInputTarArchive(inputStream, null); - } - - /// - /// The InputStream based constructors create a TarArchive for the - /// purposes of extracting or listing a tar archive. Thus, use - /// these constructors when you wish to extract files from or list - /// the contents of an existing tar archive. - /// - /// The stream to retrieve archive data from. - /// The used for the Name fields, or null for ASCII only - /// Returns a new suitable for reading from. - public static TarArchive CreateInputTarArchive(Stream inputStream, Encoding nameEncoding) - { - if (inputStream == null) - { - throw new ArgumentNullException(nameof(inputStream)); - } - - var tarStream = inputStream as TarInputStream; - - TarArchive result; - if (tarStream != null) - { - result = new TarArchive(tarStream); - } - else - { - result = CreateInputTarArchive(inputStream, TarBuffer.DefaultBlockFactor, nameEncoding); - } - return result; - } - - /// - /// Create TarArchive for reading setting block factor - /// - /// A stream containing the tar archive contents - /// The blocking factor to apply - /// Returns a suitable for reading. - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public static TarArchive CreateInputTarArchive(Stream inputStream, int blockFactor) - { - return CreateInputTarArchive(inputStream, blockFactor, null); - } - - /// - /// Create TarArchive for reading setting block factor - /// - /// A stream containing the tar archive contents - /// The blocking factor to apply - /// The used for the Name fields, or null for ASCII only - /// Returns a suitable for reading. - public static TarArchive CreateInputTarArchive(Stream inputStream, int blockFactor, Encoding nameEncoding) - { - if (inputStream == null) - { - throw new ArgumentNullException(nameof(inputStream)); - } - - if (inputStream is TarInputStream) - { - throw new ArgumentException("TarInputStream not valid"); - } - - return new TarArchive(new TarInputStream(inputStream, blockFactor, nameEncoding)); - } - /// - /// Create a TarArchive for writing to, using the default blocking factor - /// - /// The to write to - /// The used for the Name fields, or null for ASCII only - /// Returns a suitable for writing. - public static TarArchive CreateOutputTarArchive(Stream outputStream, Encoding nameEncoding) - { - if (outputStream == null) - { - throw new ArgumentNullException(nameof(outputStream)); - } - - var tarStream = outputStream as TarOutputStream; - - TarArchive result; - if (tarStream != null) - { - result = new TarArchive(tarStream); - } - else - { - result = CreateOutputTarArchive(outputStream, TarBuffer.DefaultBlockFactor, nameEncoding); - } - return result; - } - /// - /// Create a TarArchive for writing to, using the default blocking factor - /// - /// The to write to - /// Returns a suitable for writing. - public static TarArchive CreateOutputTarArchive(Stream outputStream) - { - return CreateOutputTarArchive(outputStream, null); - } - - /// - /// Create a tar archive for writing. - /// - /// The stream to write to - /// The blocking factor to use for buffering. - /// Returns a suitable for writing. - public static TarArchive CreateOutputTarArchive(Stream outputStream, int blockFactor) - { - return CreateOutputTarArchive(outputStream, blockFactor, null); - } - /// - /// Create a tar archive for writing. - /// - /// The stream to write to - /// The blocking factor to use for buffering. - /// The used for the Name fields, or null for ASCII only - /// Returns a suitable for writing. - public static TarArchive CreateOutputTarArchive(Stream outputStream, int blockFactor, Encoding nameEncoding) - { - if (outputStream == null) - { - throw new ArgumentNullException(nameof(outputStream)); - } - - if (outputStream is TarOutputStream) - { - throw new ArgumentException("TarOutputStream is not valid"); - } - - return new TarArchive(new TarOutputStream(outputStream, blockFactor, nameEncoding)); - } - - #endregion Static factory methods - - /// - /// Set the flag that determines whether existing files are - /// kept, or overwritten during extraction. - /// - /// - /// If true, do not overwrite existing files. - /// - public void SetKeepOldFiles(bool keepExistingFiles) - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - keepOldFiles = keepExistingFiles; - } - - /// - /// Get/set the ascii file translation flag. If ascii file translation - /// is true, then the file is checked to see if it a binary file or not. - /// If the flag is true and the test indicates it is ascii text - /// file, it will be translated. The translation converts the local - /// operating system's concept of line ends into the UNIX line end, - /// '\n', which is the defacto standard for a TAR archive. This makes - /// text files compatible with UNIX. - /// - public bool AsciiTranslate - { - get - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - return asciiTranslate; - } - - set - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - asciiTranslate = value; - } - } - - /// - /// Set the ascii file translation flag. - /// - /// - /// If true, translate ascii text files. - /// - [Obsolete("Use the AsciiTranslate property")] - public void SetAsciiTranslation(bool translateAsciiFiles) - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - asciiTranslate = translateAsciiFiles; - } - - /// - /// PathPrefix is added to entry names as they are written if the value is not null. - /// A slash character is appended after PathPrefix - /// - public string PathPrefix - { - get - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - return pathPrefix; - } - - set - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - pathPrefix = value; - } - } - - /// - /// RootPath is removed from entry names if it is found at the - /// beginning of the name. - /// - public string RootPath - { - get - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - return rootPath; - } - - set - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - // Convert to forward slashes for matching. Trim trailing / for correct final path - rootPath = value.Replace('\\', '/').TrimEnd('/'); - } - } - - /// - /// Set user and group information that will be used to fill in the - /// tar archive's entry headers. This information is based on that available - /// for the linux operating system, which is not always available on other - /// operating systems. TarArchive allows the programmer to specify values - /// to be used in their place. - /// is set to true by this call. - /// - /// - /// The user id to use in the headers. - /// - /// - /// The user name to use in the headers. - /// - /// - /// The group id to use in the headers. - /// - /// - /// The group name to use in the headers. - /// - public void SetUserInfo(int userId, string userName, int groupId, string groupName) - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - this.userId = userId; - this.userName = userName; - this.groupId = groupId; - this.groupName = groupName; - applyUserInfoOverrides = true; - } - - /// - /// Get or set a value indicating if overrides defined by SetUserInfo should be applied. - /// - /// If overrides are not applied then the values as set in each header will be used. - public bool ApplyUserInfoOverrides - { - get - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - return applyUserInfoOverrides; - } - - set - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - applyUserInfoOverrides = value; - } - } - - /// - /// Get the archive user id. - /// See ApplyUserInfoOverrides for detail - /// on how to allow setting values on a per entry basis. - /// - /// - /// The current user id. - /// - public int UserId - { - get - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - return userId; - } - } - - /// - /// Get the archive user name. - /// See ApplyUserInfoOverrides for detail - /// on how to allow setting values on a per entry basis. - /// - /// - /// The current user name. - /// - public string UserName - { - get - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - return userName; - } - } - - /// - /// Get the archive group id. - /// See ApplyUserInfoOverrides for detail - /// on how to allow setting values on a per entry basis. - /// - /// - /// The current group id. - /// - public int GroupId - { - get - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - return groupId; - } - } - - /// - /// Get the archive group name. - /// See ApplyUserInfoOverrides for detail - /// on how to allow setting values on a per entry basis. - /// - /// - /// The current group name. - /// - public string GroupName - { - get - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - return groupName; - } - } - - /// - /// Get the archive's record size. Tar archives are composed of - /// a series of RECORDS each containing a number of BLOCKS. - /// This allowed tar archives to match the IO characteristics of - /// the physical device being used. Archives are expected - /// to be properly "blocked". - /// - /// - /// The record size this archive is using. - /// - public int RecordSize - { - get - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - if (tarIn != null) - { - return tarIn.RecordSize; - } - else if (tarOut != null) - { - return tarOut.RecordSize; - } - return TarBuffer.DefaultRecordSize; - } - } - - /// - /// Sets the IsStreamOwner property on the underlying stream. - /// Set this to false to prevent the Close of the TarArchive from closing the stream. - /// - public bool IsStreamOwner - { - set - { - if (tarIn != null) - { - tarIn.IsStreamOwner = value; - } - else - { - tarOut.IsStreamOwner = value; - } - } - } - - /// - /// Close the archive. - /// - [Obsolete("Use Close instead")] - public void CloseArchive() - { - Close(); - } - - /// - /// Perform the "list" command for the archive contents. - /// - /// NOTE That this method uses the progress event to actually list - /// the contents. If the progress display event is not set, nothing will be listed! - /// - public void ListContents() - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - while (true) - { - TarEntry entry = tarIn.GetNextEntry(); - - if (entry == null) - { - break; - } - OnProgressMessageEvent(entry, null); - } - } - - /// - /// Perform the "extract" command and extract the contents of the archive. - /// - /// - /// The destination directory into which to extract. - /// - public void ExtractContents(string destinationDirectory) - => ExtractContents(destinationDirectory, false); - - /// - /// Perform the "extract" command and extract the contents of the archive. - /// - /// - /// The destination directory into which to extract. - /// - /// Allow parent directory traversal in file paths (e.g. ../file) - public void ExtractContents(string destinationDirectory, bool allowParentTraversal) - { - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - var fullDistDir = Path.GetFullPath(destinationDirectory).TrimEnd('/', '\\'); - - while (true) - { - TarEntry entry = tarIn.GetNextEntry(); - - if (entry == null) - { - break; - } - - if (entry.TarHeader.TypeFlag == TarHeader.LF_LINK || entry.TarHeader.TypeFlag == TarHeader.LF_SYMLINK) - continue; - - ExtractEntry(fullDistDir, entry, allowParentTraversal); - } - } - - /// - /// Extract an entry from the archive. This method assumes that the - /// tarIn stream has been properly set with a call to GetNextEntry(). - /// - /// - /// The destination directory into which to extract. - /// - /// - /// The TarEntry returned by tarIn.GetNextEntry(). - /// - /// Allow parent directory traversal in file paths (e.g. ../file) - private void ExtractEntry(string destDir, TarEntry entry, bool allowParentTraversal) - { - OnProgressMessageEvent(entry, null); - - string name = entry.Name; - - if (Path.IsPathRooted(name)) - { - // NOTE: - // for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt - name = name.Substring(Path.GetPathRoot(name).Length); - } - - name = name.Replace('/', Path.DirectorySeparatorChar); - - string destFile = Path.Combine(destDir, name); - var destFileDir = Path.GetDirectoryName(Path.GetFullPath(destFile)) ?? ""; - - if (!allowParentTraversal && !destFileDir.StartsWith(destDir, StringComparison.InvariantCultureIgnoreCase)) - { - throw new InvalidNameException("Parent traversal in paths is not allowed"); - } - - if (entry.IsDirectory) - { - EnsureDirectoryExists(destFile); - } - else - { - string parentDirectory = Path.GetDirectoryName(destFile); - EnsureDirectoryExists(parentDirectory); - - bool process = true; - var fileInfo = new FileInfo(destFile); - if (fileInfo.Exists) - { - if (keepOldFiles) - { - OnProgressMessageEvent(entry, "Destination file already exists"); - process = false; - } - else if ((fileInfo.Attributes & FileAttributes.ReadOnly) != 0) - { - OnProgressMessageEvent(entry, "Destination file already exists, and is read-only"); - process = false; - } - } - - if (process) - { - using (var outputStream = File.Create(destFile)) - { - if (this.asciiTranslate) - { - // May need to translate the file. - ExtractAndTranslateEntry(destFile, outputStream); - } - else - { - // If translation is disabled, just copy the entry across directly. - tarIn.CopyEntryContents(outputStream); - } - } - } - } - } - - // Extract a TAR entry, and perform an ASCII translation if required. - private void ExtractAndTranslateEntry(string destFile, Stream outputStream) - { - bool asciiTrans = !IsBinary(destFile); - - if (asciiTrans) - { - using (var outw = new StreamWriter(outputStream, new UTF8Encoding(false), 1024, true)) - { - byte[] rdbuf = new byte[32 * 1024]; - - while (true) - { - int numRead = tarIn.Read(rdbuf, 0, rdbuf.Length); - - if (numRead <= 0) - { - break; - } - - for (int off = 0, b = 0; b < numRead; ++b) - { - if (rdbuf[b] == 10) - { - string s = Encoding.ASCII.GetString(rdbuf, off, (b - off)); - outw.WriteLine(s); - off = b + 1; - } - } - } - } - } - else - { - // No translation required. - tarIn.CopyEntryContents(outputStream); - } - } - - /// - /// Write an entry to the archive. This method will call the putNextEntry - /// and then write the contents of the entry, and finally call closeEntry() - /// for entries that are files. For directories, it will call putNextEntry(), - /// and then, if the recurse flag is true, process each entry that is a - /// child of the directory. - /// - /// - /// The TarEntry representing the entry to write to the archive. - /// - /// - /// If true, process the children of directory entries. - /// - public void WriteEntry(TarEntry sourceEntry, bool recurse) - { - if (sourceEntry == null) - { - throw new ArgumentNullException(nameof(sourceEntry)); - } - - if (isDisposed) - { - throw new ObjectDisposedException("TarArchive"); - } - - try - { - if (recurse) - { - TarHeader.SetValueDefaults(sourceEntry.UserId, sourceEntry.UserName, - sourceEntry.GroupId, sourceEntry.GroupName); - } - WriteEntryCore(sourceEntry, recurse); - } - finally - { - if (recurse) - { - TarHeader.RestoreSetValues(); - } - } - } - - /// - /// Write an entry to the archive. This method will call the putNextEntry - /// and then write the contents of the entry, and finally call closeEntry() - /// for entries that are files. For directories, it will call putNextEntry(), - /// and then, if the recurse flag is true, process each entry that is a - /// child of the directory. - /// - /// - /// The TarEntry representing the entry to write to the archive. - /// - /// - /// If true, process the children of directory entries. - /// - private void WriteEntryCore(TarEntry sourceEntry, bool recurse) - { - string tempFileName = null; - string entryFilename = sourceEntry.File; - - var entry = (TarEntry)sourceEntry.Clone(); - - if (applyUserInfoOverrides) - { - entry.GroupId = groupId; - entry.GroupName = groupName; - entry.UserId = userId; - entry.UserName = userName; - } - - OnProgressMessageEvent(entry, null); - - if (asciiTranslate && !entry.IsDirectory) - { - if (!IsBinary(entryFilename)) - { - tempFileName = PathUtils.GetTempFileName(); - - using (StreamReader inStream = File.OpenText(entryFilename)) - { - using (Stream outStream = File.Create(tempFileName)) - { - while (true) - { - string line = inStream.ReadLine(); - if (line == null) - { - break; - } - byte[] data = Encoding.ASCII.GetBytes(line); - outStream.Write(data, 0, data.Length); - outStream.WriteByte((byte)'\n'); - } - - outStream.Flush(); - } - } - - entry.Size = new FileInfo(tempFileName).Length; - entryFilename = tempFileName; - } - } - - string newName = null; - - if (!String.IsNullOrEmpty(rootPath)) - { - if (entry.Name.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) - { - newName = entry.Name.Substring(rootPath.Length + 1); - } - } - - if (pathPrefix != null) - { - newName = (newName == null) ? pathPrefix + "/" + entry.Name : pathPrefix + "/" + newName; - } - - if (newName != null) - { - entry.Name = newName; - } - - tarOut.PutNextEntry(entry); - - if (entry.IsDirectory) - { - if (recurse) - { - TarEntry[] list = entry.GetDirectoryEntries(); - for (int i = 0; i < list.Length; ++i) - { - WriteEntryCore(list[i], recurse); - } - } - } - else - { - using (Stream inputStream = File.OpenRead(entryFilename)) - { - byte[] localBuffer = new byte[32 * 1024]; - while (true) - { - int numRead = inputStream.Read(localBuffer, 0, localBuffer.Length); - - if (numRead <= 0) - { - break; - } - - tarOut.Write(localBuffer, 0, numRead); - } - } - - if (!string.IsNullOrEmpty(tempFileName)) - { - File.Delete(tempFileName); - } - - tarOut.CloseEntry(); - } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases the unmanaged resources used by the FileStream and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - if (!isDisposed) - { - isDisposed = true; - if (disposing) - { - if (tarOut != null) - { - tarOut.Flush(); - tarOut.Dispose(); - } - - if (tarIn != null) - { - tarIn.Dispose(); - } - } - } - } - - /// - /// Closes the archive and releases any associated resources. - /// - public virtual void Close() - { - Dispose(true); - } - - /// - /// Ensures that resources are freed and other cleanup operations are performed - /// when the garbage collector reclaims the . - /// - ~TarArchive() - { - Dispose(false); - } - - private static void EnsureDirectoryExists(string directoryName) - { - if (!Directory.Exists(directoryName)) - { - try - { - Directory.CreateDirectory(directoryName); - } - catch (Exception e) - { - throw new TarException("Exception creating directory '" + directoryName + "', " + e.Message, e); - } - } - } - - // TODO: TarArchive - Is there a better way to test for a text file? - // It no longer reads entire files into memory but is still a weak test! - // This assumes that byte values 0-7, 14-31 or 255 are binary - // and that all non text files contain one of these values - private static bool IsBinary(string filename) - { - using (FileStream fs = File.OpenRead(filename)) - { - int sampleSize = Math.Min(4096, (int)fs.Length); - byte[] content = new byte[sampleSize]; - - int bytesRead = fs.Read(content, 0, sampleSize); - - for (int i = 0; i < bytesRead; ++i) - { - byte b = content[i]; - if ((b < 8) || ((b > 13) && (b < 32)) || (b == 255)) - { - return true; - } - } - } - return false; - } - - #region Instance Fields - - private bool keepOldFiles; - private bool asciiTranslate; - - private int userId; - private string userName = string.Empty; - private int groupId; - private string groupName = string.Empty; - - private string rootPath; - private string pathPrefix; - - private bool applyUserInfoOverrides; - - private TarInputStream tarIn; - private TarOutputStream tarOut; - private bool isDisposed; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Tar/TarBuffer.cs b/ICSharpCode.SharpZipLib/Tar/TarBuffer.cs deleted file mode 100644 index 744c13189a3f..000000000000 --- a/ICSharpCode.SharpZipLib/Tar/TarBuffer.cs +++ /dev/null @@ -1,599 +0,0 @@ -using System; -using System.IO; - -namespace ICSharpCode.SharpZipLib.Tar -{ - /// - /// The TarBuffer class implements the tar archive concept - /// of a buffered input stream. This concept goes back to the - /// days of blocked tape drives and special io devices. In the - /// C# universe, the only real function that this class - /// performs is to ensure that files have the correct "record" - /// size, or other tars will complain. - ///

- /// You should never have a need to access this class directly. - /// TarBuffers are created by Tar IO Streams. - ///

- ///
- public class TarBuffer - { - /* A quote from GNU tar man file on blocking and records - A `tar' archive file contains a series of blocks. Each block - contains `BLOCKSIZE' bytes. Although this format may be thought of as - being on magnetic tape, other media are often used. - - Each file archived is represented by a header block which describes - the file, followed by zero or more blocks which give the contents of - the file. At the end of the archive file there may be a block filled - with binary zeros as an end-of-file marker. A reasonable system should - write a block of zeros at the end, but must not assume that such a - block exists when reading an archive. - - The blocks may be "blocked" for physical I/O operations. Each - record of N blocks is written with a single 'write ()' - operation. On magnetic tapes, the result of such a write is a single - record. When writing an archive, the last record of blocks should be - written at the full size, with blocks after the zero block containing - all zeros. When reading an archive, a reasonable system should - properly handle an archive whose last record is shorter than the rest, - or which contains garbage records after a zero block. - */ - - #region Constants - - /// - /// The size of a block in a tar archive in bytes. - /// - /// This is 512 bytes. - public const int BlockSize = 512; - - /// - /// The number of blocks in a default record. - /// - /// - /// The default value is 20 blocks per record. - /// - public const int DefaultBlockFactor = 20; - - /// - /// The size in bytes of a default record. - /// - /// - /// The default size is 10KB. - /// - public const int DefaultRecordSize = BlockSize * DefaultBlockFactor; - - #endregion Constants - - /// - /// Get the record size for this buffer - /// - /// The record size in bytes. - /// This is equal to the multiplied by the - public int RecordSize - { - get - { - return recordSize; - } - } - - /// - /// Get the TAR Buffer's record size. - /// - /// The record size in bytes. - /// This is equal to the multiplied by the - [Obsolete("Use RecordSize property instead")] - public int GetRecordSize() - { - return recordSize; - } - - /// - /// Get the Blocking factor for the buffer - /// - /// This is the number of blocks in each record. - public int BlockFactor - { - get - { - return blockFactor; - } - } - - /// - /// Get the TAR Buffer's block factor - /// - /// The block factor; the number of blocks per record. - [Obsolete("Use BlockFactor property instead")] - public int GetBlockFactor() - { - return blockFactor; - } - - /// - /// Construct a default TarBuffer - /// - protected TarBuffer() - { - } - - /// - /// Create TarBuffer for reading with default BlockFactor - /// - /// Stream to buffer - /// A new suitable for input. - public static TarBuffer CreateInputTarBuffer(Stream inputStream) - { - if (inputStream == null) - { - throw new ArgumentNullException(nameof(inputStream)); - } - - return CreateInputTarBuffer(inputStream, DefaultBlockFactor); - } - - /// - /// Construct TarBuffer for reading inputStream setting BlockFactor - /// - /// Stream to buffer - /// Blocking factor to apply - /// A new suitable for input. - public static TarBuffer CreateInputTarBuffer(Stream inputStream, int blockFactor) - { - if (inputStream == null) - { - throw new ArgumentNullException(nameof(inputStream)); - } - - if (blockFactor <= 0) - { - throw new ArgumentOutOfRangeException(nameof(blockFactor), "Factor cannot be negative"); - } - - var tarBuffer = new TarBuffer(); - tarBuffer.inputStream = inputStream; - tarBuffer.outputStream = null; - tarBuffer.Initialize(blockFactor); - - return tarBuffer; - } - - /// - /// Construct TarBuffer for writing with default BlockFactor - /// - /// output stream for buffer - /// A new suitable for output. - public static TarBuffer CreateOutputTarBuffer(Stream outputStream) - { - if (outputStream == null) - { - throw new ArgumentNullException(nameof(outputStream)); - } - - return CreateOutputTarBuffer(outputStream, DefaultBlockFactor); - } - - /// - /// Construct TarBuffer for writing Tar output to streams. - /// - /// Output stream to write to. - /// Blocking factor to apply - /// A new suitable for output. - public static TarBuffer CreateOutputTarBuffer(Stream outputStream, int blockFactor) - { - if (outputStream == null) - { - throw new ArgumentNullException(nameof(outputStream)); - } - - if (blockFactor <= 0) - { - throw new ArgumentOutOfRangeException(nameof(blockFactor), "Factor cannot be negative"); - } - - var tarBuffer = new TarBuffer(); - tarBuffer.inputStream = null; - tarBuffer.outputStream = outputStream; - tarBuffer.Initialize(blockFactor); - - return tarBuffer; - } - - /// - /// Initialization common to all constructors. - /// - private void Initialize(int archiveBlockFactor) - { - blockFactor = archiveBlockFactor; - recordSize = archiveBlockFactor * BlockSize; - recordBuffer = new byte[RecordSize]; - - if (inputStream != null) - { - currentRecordIndex = -1; - currentBlockIndex = BlockFactor; - } - else - { - currentRecordIndex = 0; - currentBlockIndex = 0; - } - } - - /// - /// Determine if an archive block indicates End of Archive. End of - /// archive is indicated by a block that consists entirely of null bytes. - /// All remaining blocks for the record should also be null's - /// However some older tars only do a couple of null blocks (Old GNU tar for one) - /// and also partial records - /// - /// The data block to check. - /// Returns true if the block is an EOF block; false otherwise. - [Obsolete("Use IsEndOfArchiveBlock instead")] - public bool IsEOFBlock(byte[] block) - { - if (block == null) - { - throw new ArgumentNullException(nameof(block)); - } - - if (block.Length != BlockSize) - { - throw new ArgumentException("block length is invalid"); - } - - for (int i = 0; i < BlockSize; ++i) - { - if (block[i] != 0) - { - return false; - } - } - - return true; - } - - /// - /// Determine if an archive block indicates the End of an Archive has been reached. - /// End of archive is indicated by a block that consists entirely of null bytes. - /// All remaining blocks for the record should also be null's - /// However some older tars only do a couple of null blocks (Old GNU tar for one) - /// and also partial records - /// - /// The data block to check. - /// Returns true if the block is an EOF block; false otherwise. - public static bool IsEndOfArchiveBlock(byte[] block) - { - if (block == null) - { - throw new ArgumentNullException(nameof(block)); - } - - if (block.Length != BlockSize) - { - throw new ArgumentException("block length is invalid"); - } - - for (int i = 0; i < BlockSize; ++i) - { - if (block[i] != 0) - { - return false; - } - } - - return true; - } - - /// - /// Skip over a block on the input stream. - /// - public void SkipBlock() - { - if (inputStream == null) - { - throw new TarException("no input stream defined"); - } - - if (currentBlockIndex >= BlockFactor) - { - if (!ReadRecord()) - { - throw new TarException("Failed to read a record"); - } - } - - currentBlockIndex++; - } - - /// - /// Read a block from the input stream. - /// - /// - /// The block of data read. - /// - public byte[] ReadBlock() - { - if (inputStream == null) - { - throw new TarException("TarBuffer.ReadBlock - no input stream defined"); - } - - if (currentBlockIndex >= BlockFactor) - { - if (!ReadRecord()) - { - throw new TarException("Failed to read a record"); - } - } - - byte[] result = new byte[BlockSize]; - - Array.Copy(recordBuffer, (currentBlockIndex * BlockSize), result, 0, BlockSize); - currentBlockIndex++; - return result; - } - - /// - /// Read a record from data stream. - /// - /// - /// false if End-Of-File, else true. - /// - private bool ReadRecord() - { - if (inputStream == null) - { - throw new TarException("no input stream defined"); - } - - currentBlockIndex = 0; - - int offset = 0; - int bytesNeeded = RecordSize; - - while (bytesNeeded > 0) - { - long numBytes = inputStream.Read(recordBuffer, offset, bytesNeeded); - - // - // NOTE - // We have found EOF, and the record is not full! - // - // This is a broken archive. It does not follow the standard - // blocking algorithm. However, because we are generous, and - // it requires little effort, we will simply ignore the error - // and continue as if the entire record were read. This does - // not appear to break anything upstream. We used to return - // false in this case. - // - // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix. - // - if (numBytes <= 0) - { - break; - } - - offset += (int)numBytes; - bytesNeeded -= (int)numBytes; - } - - currentRecordIndex++; - return true; - } - - /// - /// Get the current block number, within the current record, zero based. - /// - /// Block numbers are zero based values - /// - public int CurrentBlock - { - get { return currentBlockIndex; } - } - - /// - /// Gets or sets a flag indicating ownership of underlying stream. - /// When the flag is true will close the underlying stream also. - /// - /// The default value is true. - public bool IsStreamOwner { get; set; } = true; - - /// - /// Get the current block number, within the current record, zero based. - /// - /// - /// The current zero based block number. - /// - /// - /// The absolute block number = (record number * block factor) + block number. - /// - [Obsolete("Use CurrentBlock property instead")] - public int GetCurrentBlockNum() - { - return currentBlockIndex; - } - - /// - /// Get the current record number. - /// - /// - /// The current zero based record number. - /// - public int CurrentRecord - { - get { return currentRecordIndex; } - } - - /// - /// Get the current record number. - /// - /// - /// The current zero based record number. - /// - [Obsolete("Use CurrentRecord property instead")] - public int GetCurrentRecordNum() - { - return currentRecordIndex; - } - - /// - /// Write a block of data to the archive. - /// - /// - /// The data to write to the archive. - /// - public void WriteBlock(byte[] block) - { - if (block == null) - { - throw new ArgumentNullException(nameof(block)); - } - - if (outputStream == null) - { - throw new TarException("TarBuffer.WriteBlock - no output stream defined"); - } - - if (block.Length != BlockSize) - { - string errorText = string.Format("TarBuffer.WriteBlock - block to write has length '{0}' which is not the block size of '{1}'", - block.Length, BlockSize); - throw new TarException(errorText); - } - - if (currentBlockIndex >= BlockFactor) - { - WriteRecord(); - } - - Array.Copy(block, 0, recordBuffer, (currentBlockIndex * BlockSize), BlockSize); - currentBlockIndex++; - } - - /// - /// Write an archive record to the archive, where the record may be - /// inside of a larger array buffer. The buffer must be "offset plus - /// record size" long. - /// - /// - /// The buffer containing the record data to write. - /// - /// - /// The offset of the record data within buffer. - /// - public void WriteBlock(byte[] buffer, int offset) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (outputStream == null) - { - throw new TarException("TarBuffer.WriteBlock - no output stream defined"); - } - - if ((offset < 0) || (offset >= buffer.Length)) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - if ((offset + BlockSize) > buffer.Length) - { - string errorText = string.Format("TarBuffer.WriteBlock - record has length '{0}' with offset '{1}' which is less than the record size of '{2}'", - buffer.Length, offset, recordSize); - throw new TarException(errorText); - } - - if (currentBlockIndex >= BlockFactor) - { - WriteRecord(); - } - - Array.Copy(buffer, offset, recordBuffer, (currentBlockIndex * BlockSize), BlockSize); - - currentBlockIndex++; - } - - /// - /// Write a TarBuffer record to the archive. - /// - private void WriteRecord() - { - if (outputStream == null) - { - throw new TarException("TarBuffer.WriteRecord no output stream defined"); - } - - outputStream.Write(recordBuffer, 0, RecordSize); - outputStream.Flush(); - - currentBlockIndex = 0; - currentRecordIndex++; - } - - /// - /// WriteFinalRecord writes the current record buffer to output any unwritten data is present. - /// - /// Any trailing bytes are set to zero which is by definition correct behaviour - /// for the end of a tar stream. - private void WriteFinalRecord() - { - if (outputStream == null) - { - throw new TarException("TarBuffer.WriteFinalRecord no output stream defined"); - } - - if (currentBlockIndex > 0) - { - int dataBytes = currentBlockIndex * BlockSize; - Array.Clear(recordBuffer, dataBytes, RecordSize - dataBytes); - WriteRecord(); - } - - outputStream.Flush(); - } - - /// - /// Close the TarBuffer. If this is an output buffer, also flush the - /// current block before closing. - /// - public void Close() - { - if (outputStream != null) - { - WriteFinalRecord(); - - if (IsStreamOwner) - { - outputStream.Dispose(); - } - outputStream = null; - } - else if (inputStream != null) - { - if (IsStreamOwner) - { - inputStream.Dispose(); - } - inputStream = null; - } - } - - #region Instance Fields - - private Stream inputStream; - private Stream outputStream; - - private byte[] recordBuffer; - private int currentBlockIndex; - private int currentRecordIndex; - - private int recordSize = DefaultRecordSize; - private int blockFactor = DefaultBlockFactor; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Tar/TarEntry.cs b/ICSharpCode.SharpZipLib/Tar/TarEntry.cs deleted file mode 100644 index 262c12ad34b2..000000000000 --- a/ICSharpCode.SharpZipLib/Tar/TarEntry.cs +++ /dev/null @@ -1,598 +0,0 @@ -using System; -using System.IO; -using System.Text; -using ICSharpCode.SharpZipLib.Core; - -namespace ICSharpCode.SharpZipLib.Tar -{ - /// - /// This class represents an entry in a Tar archive. It consists - /// of the entry's header, as well as the entry's File. Entries - /// can be instantiated in one of three ways, depending on how - /// they are to be used. - ///

- /// TarEntries that are created from the header bytes read from - /// an archive are instantiated with the TarEntry( byte[] ) - /// constructor. These entries will be used when extracting from - /// or listing the contents of an archive. These entries have their - /// header filled in using the header bytes. They also set the File - /// to null, since they reference an archive entry not a file.

- ///

- /// TarEntries that are created from files that are to be written - /// into an archive are instantiated with the CreateEntryFromFile(string) - /// pseudo constructor. These entries have their header filled in using - /// the File's information. They also keep a reference to the File - /// for convenience when writing entries.

- ///

- /// Finally, TarEntries can be constructed from nothing but a name. - /// This allows the programmer to construct the entry by hand, for - /// instance when only an InputStream is available for writing to - /// the archive, and the header information is constructed from - /// other information. In this case the header fields are set to - /// defaults and the File is set to null.

- /// - ///
- public class TarEntry - { - #region Constructors - - /// - /// Initialise a default instance of . - /// - private TarEntry() - { - header = new TarHeader(); - } - - /// - /// Construct an entry from an archive's header bytes. File is set - /// to null. - /// - /// - /// The header bytes from a tar archive entry. - /// - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public TarEntry(byte[] headerBuffer) : this(headerBuffer, null) - { - } - - /// - /// Construct an entry from an archive's header bytes. File is set - /// to null. - /// - /// - /// The header bytes from a tar archive entry. - /// - /// - /// The used for the Name fields, or null for ASCII only - /// - public TarEntry(byte[] headerBuffer, Encoding nameEncoding) - { - header = new TarHeader(); - header.ParseBuffer(headerBuffer, nameEncoding); - } - - /// - /// Construct a TarEntry using the header provided - /// - /// Header details for entry - public TarEntry(TarHeader header) - { - if (header == null) - { - throw new ArgumentNullException(nameof(header)); - } - - this.header = (TarHeader)header.Clone(); - } - - #endregion Constructors - - #region ICloneable Members - - /// - /// Clone this tar entry. - /// - /// Returns a clone of this entry. - public object Clone() - { - var entry = new TarEntry(); - entry.file = file; - entry.header = (TarHeader)header.Clone(); - entry.Name = Name; - return entry; - } - - #endregion ICloneable Members - - /// - /// Construct an entry with only a name. - /// This allows the programmer to construct the entry's header "by hand". - /// - /// The name to use for the entry - /// Returns the newly created - public static TarEntry CreateTarEntry(string name) - { - var entry = new TarEntry(); - TarEntry.NameTarHeader(entry.header, name); - return entry; - } - - /// - /// Construct an entry for a file. File is set to file, and the - /// header is constructed from information from the file. - /// - /// The file name that the entry represents. - /// Returns the newly created - public static TarEntry CreateEntryFromFile(string fileName) - { - var entry = new TarEntry(); - entry.GetFileTarHeader(entry.header, fileName); - return entry; - } - - /// - /// Determine if the two entries are equal. Equality is determined - /// by the header names being equal. - /// - /// The to compare with the current Object. - /// - /// True if the entries are equal; false if not. - /// - public override bool Equals(object obj) - { - var localEntry = obj as TarEntry; - - if (localEntry != null) - { - return Name.Equals(localEntry.Name); - } - return false; - } - - /// - /// Derive a Hash value for the current - /// - /// A Hash code for the current - public override int GetHashCode() - { - return Name.GetHashCode(); - } - - /// - /// Determine if the given entry is a descendant of this entry. - /// Descendancy is determined by the name of the descendant - /// starting with this entry's name. - /// - /// - /// Entry to be checked as a descendent of this. - /// - /// - /// True if entry is a descendant of this. - /// - public bool IsDescendent(TarEntry toTest) - { - if (toTest == null) - { - throw new ArgumentNullException(nameof(toTest)); - } - - return toTest.Name.StartsWith(Name, StringComparison.Ordinal); - } - - /// - /// Get this entry's header. - /// - /// - /// This entry's TarHeader. - /// - public TarHeader TarHeader - { - get - { - return header; - } - } - - /// - /// Get/Set this entry's name. - /// - public string Name - { - get - { - return header.Name; - } - set - { - header.Name = value; - } - } - - /// - /// Get/set this entry's user id. - /// - public int UserId - { - get - { - return header.UserId; - } - set - { - header.UserId = value; - } - } - - /// - /// Get/set this entry's group id. - /// - public int GroupId - { - get - { - return header.GroupId; - } - set - { - header.GroupId = value; - } - } - - /// - /// Get/set this entry's user name. - /// - public string UserName - { - get - { - return header.UserName; - } - set - { - header.UserName = value; - } - } - - /// - /// Get/set this entry's group name. - /// - public string GroupName - { - get - { - return header.GroupName; - } - set - { - header.GroupName = value; - } - } - - /// - /// Convenience method to set this entry's group and user ids. - /// - /// - /// This entry's new user id. - /// - /// - /// This entry's new group id. - /// - public void SetIds(int userId, int groupId) - { - UserId = userId; - GroupId = groupId; - } - - /// - /// Convenience method to set this entry's group and user names. - /// - /// - /// This entry's new user name. - /// - /// - /// This entry's new group name. - /// - public void SetNames(string userName, string groupName) - { - UserName = userName; - GroupName = groupName; - } - - /// - /// Get/Set the modification time for this entry - /// - public DateTime ModTime - { - get - { - return header.ModTime; - } - set - { - header.ModTime = value; - } - } - - /// - /// Get this entry's file. - /// - /// - /// This entry's file. - /// - public string File - { - get - { - return file; - } - } - - /// - /// Get/set this entry's recorded file size. - /// - public long Size - { - get - { - return header.Size; - } - set - { - header.Size = value; - } - } - - /// - /// Return true if this entry represents a directory, false otherwise - /// - /// - /// True if this entry is a directory. - /// - public bool IsDirectory - { - get - { - if (file != null) - { - return Directory.Exists(file); - } - - if (header != null) - { - if ((header.TypeFlag == TarHeader.LF_DIR) || Name.EndsWith("/", StringComparison.Ordinal)) - { - return true; - } - } - return false; - } - } - - /// - /// Fill in a TarHeader with information from a File. - /// - /// - /// The TarHeader to fill in. - /// - /// - /// The file from which to get the header information. - /// - public void GetFileTarHeader(TarHeader header, string file) - { - if (header == null) - { - throw new ArgumentNullException(nameof(header)); - } - - if (file == null) - { - throw new ArgumentNullException(nameof(file)); - } - - this.file = file; - - // bugfix from torhovl from #D forum: - string name = file; - - // 23-Jan-2004 GnuTar allows device names in path where the name is not local to the current directory - if (name.IndexOf(Directory.GetCurrentDirectory(), StringComparison.Ordinal) == 0) - { - name = name.Substring(Directory.GetCurrentDirectory().Length); - } - - /* - if (Path.DirectorySeparatorChar == '\\') - { - // check if the OS is Windows - // Strip off drive letters! - if (name.Length > 2) - { - char ch1 = name[0]; - char ch2 = name[1]; - - if (ch2 == ':' && Char.IsLetter(ch1)) - { - name = name.Substring(2); - } - } - } - */ - - name = name.Replace(Path.DirectorySeparatorChar, '/'); - - // No absolute pathnames - // Windows (and Posix?) paths can start with UNC style "\\NetworkDrive\", - // so we loop on starting /'s. - while (name.StartsWith("/", StringComparison.Ordinal)) - { - name = name.Substring(1); - } - - header.LinkName = String.Empty; - header.Name = name; - - if (Directory.Exists(file)) - { - header.Mode = 1003; // Magic number for security access for a UNIX filesystem - header.TypeFlag = TarHeader.LF_DIR; - if ((header.Name.Length == 0) || header.Name[header.Name.Length - 1] != '/') - { - header.Name = header.Name + "/"; - } - - header.Size = 0; - } - else - { - header.Mode = 33216; // Magic number for security access for a UNIX filesystem - header.TypeFlag = TarHeader.LF_NORMAL; - header.Size = new FileInfo(file.Replace('/', Path.DirectorySeparatorChar)).Length; - } - - header.ModTime = System.IO.File.GetLastWriteTime(file.Replace('/', Path.DirectorySeparatorChar)).ToUniversalTime(); - header.DevMajor = 0; - header.DevMinor = 0; - } - - /// - /// Get entries for all files present in this entries directory. - /// If this entry doesnt represent a directory zero entries are returned. - /// - /// - /// An array of TarEntry's for this entry's children. - /// - public TarEntry[] GetDirectoryEntries() - { - if ((file == null) || !Directory.Exists(file)) - { - return Empty.Array(); - } - - string[] list = Directory.GetFileSystemEntries(file); - TarEntry[] result = new TarEntry[list.Length]; - - for (int i = 0; i < list.Length; ++i) - { - result[i] = TarEntry.CreateEntryFromFile(list[i]); - } - - return result; - } - - /// - /// Write an entry's header information to a header buffer. - /// - /// - /// The tar entry header buffer to fill in. - /// - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public void WriteEntryHeader(byte[] outBuffer) - { - WriteEntryHeader(outBuffer, null); - } - - /// - /// Write an entry's header information to a header buffer. - /// - /// - /// The tar entry header buffer to fill in. - /// - /// - /// The used for the Name fields, or null for ASCII only - /// - public void WriteEntryHeader(byte[] outBuffer, Encoding nameEncoding) - { - header.WriteHeader(outBuffer, nameEncoding); - } - - /// - /// Convenience method that will modify an entry's name directly - /// in place in an entry header buffer byte array. - /// - /// - /// The buffer containing the entry header to modify. - /// - /// - /// The new name to place into the header buffer. - /// - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - static public void AdjustEntryName(byte[] buffer, string newName) - { - AdjustEntryName(buffer, newName, null); - } - - /// - /// Convenience method that will modify an entry's name directly - /// in place in an entry header buffer byte array. - /// - /// - /// The buffer containing the entry header to modify. - /// - /// - /// The new name to place into the header buffer. - /// - /// - /// The used for the Name fields, or null for ASCII only - /// - static public void AdjustEntryName(byte[] buffer, string newName, Encoding nameEncoding) - { - TarHeader.GetNameBytes(newName, buffer, 0, TarHeader.NAMELEN, nameEncoding); - } - - /// - /// Fill in a TarHeader given only the entry's name. - /// - /// - /// The TarHeader to fill in. - /// - /// - /// The tar entry name. - /// - static public void NameTarHeader(TarHeader header, string name) - { - if (header == null) - { - throw new ArgumentNullException(nameof(header)); - } - - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - bool isDir = name.EndsWith("/", StringComparison.Ordinal); - - header.Name = name; - header.Mode = isDir ? 1003 : 33216; - header.UserId = 0; - header.GroupId = 0; - header.Size = 0; - - header.ModTime = DateTime.UtcNow; - - header.TypeFlag = isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL; - - header.LinkName = String.Empty; - header.UserName = String.Empty; - header.GroupName = String.Empty; - - header.DevMajor = 0; - header.DevMinor = 0; - } - - #region Instance Fields - - /// - /// The name of the file this entry represents or null if the entry is not based on a file. - /// - private string file; - - /// - /// The entry's header information. - /// - private TarHeader header; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Tar/TarException.cs b/ICSharpCode.SharpZipLib/Tar/TarException.cs deleted file mode 100644 index 9d448ca7d636..000000000000 --- a/ICSharpCode.SharpZipLib/Tar/TarException.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib.Tar -{ - /// - /// TarException represents exceptions specific to Tar classes and code. - /// - [Serializable] - public class TarException : SharpZipBaseException - { - /// - /// Initialise a new instance of . - /// - public TarException() - { - } - - /// - /// Initialise a new instance of with its message string. - /// - /// A that describes the error. - public TarException(string message) - : base(message) - { - } - - /// - /// Initialise a new instance of . - /// - /// A that describes the error. - /// The that caused this exception. - public TarException(string message, Exception innerException) - : base(message, innerException) - { - } - - /// - /// Initializes a new instance of the TarException class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected TarException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Tar/TarExtendedHeaderReader.cs b/ICSharpCode.SharpZipLib/Tar/TarExtendedHeaderReader.cs deleted file mode 100644 index d1d438ad070e..000000000000 --- a/ICSharpCode.SharpZipLib/Tar/TarExtendedHeaderReader.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using System.Text; - -namespace ICSharpCode.SharpZipLib.Tar -{ - /// - /// Reads the extended header of a Tar stream - /// - public class TarExtendedHeaderReader - { - private const byte LENGTH = 0; - private const byte KEY = 1; - private const byte VALUE = 2; - private const byte END = 3; - - private readonly Dictionary headers = new Dictionary(); - - private string[] headerParts = new string[3]; - - private int bbIndex; - private byte[] byteBuffer; - private char[] charBuffer; - - private readonly StringBuilder sb = new StringBuilder(); - private readonly Decoder decoder = Encoding.UTF8.GetDecoder(); - - private int state = LENGTH; - - private static readonly byte[] StateNext = new[] { (byte)' ', (byte)'=', (byte)'\n' }; - - /// - /// Creates a new . - /// - public TarExtendedHeaderReader() - { - ResetBuffers(); - } - - /// - /// Read bytes from - /// - /// - /// - public void Read(byte[] buffer, int length) - { - for (int i = 0; i < length; i++) - { - byte next = buffer[i]; - - if (next == StateNext[state]) - { - Flush(); - headerParts[state] = sb.ToString(); - sb.Clear(); - - if (++state == END) - { - headers.Add(headerParts[KEY], headerParts[VALUE]); - headerParts = new string[3]; - state = LENGTH; - } - } - else - { - byteBuffer[bbIndex++] = next; - if (bbIndex == 4) - Flush(); - } - } - } - - private void Flush() - { - decoder.Convert(byteBuffer, 0, bbIndex, charBuffer, 0, 4, false, out int bytesUsed, out int charsUsed, out bool completed); - - sb.Append(charBuffer, 0, charsUsed); - ResetBuffers(); - } - - private void ResetBuffers() - { - charBuffer = new char[4]; - byteBuffer = new byte[4]; - bbIndex = 0; - } - - /// - /// Returns the parsed headers as key-value strings - /// - public Dictionary Headers - { - get - { - // TODO: Check for invalid state? -NM 2018-07-01 - return headers; - } - } - } -} diff --git a/ICSharpCode.SharpZipLib/Tar/TarHeader.cs b/ICSharpCode.SharpZipLib/Tar/TarHeader.cs deleted file mode 100644 index 3bd1bdffe5d8..000000000000 --- a/ICSharpCode.SharpZipLib/Tar/TarHeader.cs +++ /dev/null @@ -1,1310 +0,0 @@ -using System; -using System.Text; - -namespace ICSharpCode.SharpZipLib.Tar -{ - /// - /// This class encapsulates the Tar Entry Header used in Tar Archives. - /// The class also holds a number of tar constants, used mostly in headers. - /// - /// - /// The tar format and its POSIX successor PAX have a long history which makes for compatability - /// issues when creating and reading files. - /// - /// This is further complicated by a large number of programs with variations on formats - /// One common issue is the handling of names longer than 100 characters. - /// GNU style long names are currently supported. - /// - /// This is the ustar (Posix 1003.1) header. - /// - /// struct header - /// { - /// char t_name[100]; // 0 Filename - /// char t_mode[8]; // 100 Permissions - /// char t_uid[8]; // 108 Numerical User ID - /// char t_gid[8]; // 116 Numerical Group ID - /// char t_size[12]; // 124 Filesize - /// char t_mtime[12]; // 136 st_mtime - /// char t_chksum[8]; // 148 Checksum - /// char t_typeflag; // 156 Type of File - /// char t_linkname[100]; // 157 Target of Links - /// char t_magic[6]; // 257 "ustar" or other... - /// char t_version[2]; // 263 Version fixed to 00 - /// char t_uname[32]; // 265 User Name - /// char t_gname[32]; // 297 Group Name - /// char t_devmajor[8]; // 329 Major for devices - /// char t_devminor[8]; // 337 Minor for devices - /// char t_prefix[155]; // 345 Prefix for t_name - /// char t_mfill[12]; // 500 Filler up to 512 - /// }; - /// - public class TarHeader - { - #region Constants - - /// - /// The length of the name field in a header buffer. - /// - public const int NAMELEN = 100; - - /// - /// The length of the mode field in a header buffer. - /// - public const int MODELEN = 8; - - /// - /// The length of the user id field in a header buffer. - /// - public const int UIDLEN = 8; - - /// - /// The length of the group id field in a header buffer. - /// - public const int GIDLEN = 8; - - /// - /// The length of the checksum field in a header buffer. - /// - public const int CHKSUMLEN = 8; - - /// - /// Offset of checksum in a header buffer. - /// - public const int CHKSUMOFS = 148; - - /// - /// The length of the size field in a header buffer. - /// - public const int SIZELEN = 12; - - /// - /// The length of the magic field in a header buffer. - /// - public const int MAGICLEN = 6; - - /// - /// The length of the version field in a header buffer. - /// - public const int VERSIONLEN = 2; - - /// - /// The length of the modification time field in a header buffer. - /// - public const int MODTIMELEN = 12; - - /// - /// The length of the user name field in a header buffer. - /// - public const int UNAMELEN = 32; - - /// - /// The length of the group name field in a header buffer. - /// - public const int GNAMELEN = 32; - - /// - /// The length of the devices field in a header buffer. - /// - public const int DEVLEN = 8; - - /// - /// The length of the name prefix field in a header buffer. - /// - public const int PREFIXLEN = 155; - - // - // LF_ constants represent the "type" of an entry - // - - /// - /// The "old way" of indicating a normal file. - /// - public const byte LF_OLDNORM = 0; - - /// - /// Normal file type. - /// - public const byte LF_NORMAL = (byte)'0'; - - /// - /// Link file type. - /// - public const byte LF_LINK = (byte)'1'; - - /// - /// Symbolic link file type. - /// - public const byte LF_SYMLINK = (byte)'2'; - - /// - /// Character device file type. - /// - public const byte LF_CHR = (byte)'3'; - - /// - /// Block device file type. - /// - public const byte LF_BLK = (byte)'4'; - - /// - /// Directory file type. - /// - public const byte LF_DIR = (byte)'5'; - - /// - /// FIFO (pipe) file type. - /// - public const byte LF_FIFO = (byte)'6'; - - /// - /// Contiguous file type. - /// - public const byte LF_CONTIG = (byte)'7'; - - /// - /// Posix.1 2001 global extended header - /// - public const byte LF_GHDR = (byte)'g'; - - /// - /// Posix.1 2001 extended header - /// - public const byte LF_XHDR = (byte)'x'; - - // POSIX allows for upper case ascii type as extensions - - /// - /// Solaris access control list file type - /// - public const byte LF_ACL = (byte)'A'; - - /// - /// GNU dir dump file type - /// This is a dir entry that contains the names of files that were in the - /// dir at the time the dump was made - /// - public const byte LF_GNU_DUMPDIR = (byte)'D'; - - /// - /// Solaris Extended Attribute File - /// - public const byte LF_EXTATTR = (byte)'E'; - - /// - /// Inode (metadata only) no file content - /// - public const byte LF_META = (byte)'I'; - - /// - /// Identifies the next file on the tape as having a long link name - /// - public const byte LF_GNU_LONGLINK = (byte)'K'; - - /// - /// Identifies the next file on the tape as having a long name - /// - public const byte LF_GNU_LONGNAME = (byte)'L'; - - /// - /// Continuation of a file that began on another volume - /// - public const byte LF_GNU_MULTIVOL = (byte)'M'; - - /// - /// For storing filenames that dont fit in the main header (old GNU) - /// - public const byte LF_GNU_NAMES = (byte)'N'; - - /// - /// GNU Sparse file - /// - public const byte LF_GNU_SPARSE = (byte)'S'; - - /// - /// GNU Tape/volume header ignore on extraction - /// - public const byte LF_GNU_VOLHDR = (byte)'V'; - - /// - /// The magic tag representing a POSIX tar archive. (would be written with a trailing NULL) - /// - public const string TMAGIC = "ustar"; - - /// - /// The magic tag representing an old GNU tar archive where version is included in magic and overwrites it - /// - public const string GNU_TMAGIC = "ustar "; - - private const long timeConversionFactor = 10000000L; // 1 tick == 100 nanoseconds - private static readonly DateTime dateTime1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0); - - #endregion Constants - - #region Constructors - - /// - /// Initialise a default TarHeader instance - /// - public TarHeader() - { - Magic = TMAGIC; - Version = " "; - - Name = ""; - LinkName = ""; - - UserId = defaultUserId; - GroupId = defaultGroupId; - UserName = defaultUser; - GroupName = defaultGroupName; - Size = 0; - } - - #endregion Constructors - - #region Properties - - /// - /// Get/set the name for this tar entry. - /// - /// Thrown when attempting to set the property to null. - public string Name - { - get { return name; } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - name = value; - } - } - - /// - /// Get the name of this entry. - /// - /// The entry's name. - [Obsolete("Use the Name property instead", true)] - public string GetName() - { - return name; - } - - /// - /// Get/set the entry's Unix style permission mode. - /// - public int Mode - { - get { return mode; } - set { mode = value; } - } - - /// - /// The entry's user id. - /// - /// - /// This is only directly relevant to unix systems. - /// The default is zero. - /// - public int UserId - { - get { return userId; } - set { userId = value; } - } - - /// - /// Get/set the entry's group id. - /// - /// - /// This is only directly relevant to linux/unix systems. - /// The default value is zero. - /// - public int GroupId - { - get { return groupId; } - set { groupId = value; } - } - - /// - /// Get/set the entry's size. - /// - /// Thrown when setting the size to less than zero. - public long Size - { - get { return size; } - set - { - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "Cannot be less than zero"); - } - size = value; - } - } - - /// - /// Get/set the entry's modification time. - /// - /// - /// The modification time is only accurate to within a second. - /// - /// Thrown when setting the date time to less than 1/1/1970. - public DateTime ModTime - { - get { return modTime; } - set - { - if (value < dateTime1970) - { - throw new ArgumentOutOfRangeException(nameof(value), "ModTime cannot be before Jan 1st 1970"); - } - modTime = new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second); - } - } - - /// - /// Get the entry's checksum. This is only valid/updated after writing or reading an entry. - /// - public int Checksum - { - get { return checksum; } - } - - /// - /// Get value of true if the header checksum is valid, false otherwise. - /// - public bool IsChecksumValid - { - get { return isChecksumValid; } - } - - /// - /// Get/set the entry's type flag. - /// - public byte TypeFlag - { - get { return typeFlag; } - set { typeFlag = value; } - } - - /// - /// The entry's link name. - /// - /// Thrown when attempting to set LinkName to null. - public string LinkName - { - get { return linkName; } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - linkName = value; - } - } - - /// - /// Get/set the entry's magic tag. - /// - /// Thrown when attempting to set Magic to null. - public string Magic - { - get { return magic; } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - magic = value; - } - } - - /// - /// The entry's version. - /// - /// Thrown when attempting to set Version to null. - public string Version - { - get - { - return version; - } - - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - version = value; - } - } - - /// - /// The entry's user name. - /// - public string UserName - { - get { return userName; } - set - { - if (value != null) - { - userName = value.Substring(0, Math.Min(UNAMELEN, value.Length)); - } - else - { - string currentUser = "user"; - if (currentUser.Length > UNAMELEN) - { - currentUser = currentUser.Substring(0, UNAMELEN); - } - userName = currentUser; - } - } - } - - /// - /// Get/set the entry's group name. - /// - /// - /// This is only directly relevant to unix systems. - /// - public string GroupName - { - get { return groupName; } - set - { - if (value == null) - { - groupName = "None"; - } - else - { - groupName = value; - } - } - } - - /// - /// Get/set the entry's major device number. - /// - public int DevMajor - { - get { return devMajor; } - set { devMajor = value; } - } - - /// - /// Get/set the entry's minor device number. - /// - public int DevMinor - { - get { return devMinor; } - set { devMinor = value; } - } - - #endregion Properties - - #region ICloneable Members - - /// - /// Create a new that is a copy of the current instance. - /// - /// A new that is a copy of the current instance. - public object Clone() - { - return this.MemberwiseClone(); - } - - #endregion ICloneable Members - - /// - /// Parse TarHeader information from a header buffer. - /// - /// - /// The tar entry header buffer to get information from. - /// - /// - /// The used for the Name field, or null for ASCII only - /// - public void ParseBuffer(byte[] header, Encoding nameEncoding) - { - if (header == null) - { - throw new ArgumentNullException(nameof(header)); - } - - int offset = 0; - - name = ParseName(header, offset, NAMELEN, nameEncoding).ToString(); - offset += NAMELEN; - - mode = (int)ParseOctal(header, offset, MODELEN); - offset += MODELEN; - - UserId = (int)ParseOctal(header, offset, UIDLEN); - offset += UIDLEN; - - GroupId = (int)ParseOctal(header, offset, GIDLEN); - offset += GIDLEN; - - Size = ParseBinaryOrOctal(header, offset, SIZELEN); - offset += SIZELEN; - - ModTime = GetDateTimeFromCTime(ParseOctal(header, offset, MODTIMELEN)); - offset += MODTIMELEN; - - checksum = (int)ParseOctal(header, offset, CHKSUMLEN); - offset += CHKSUMLEN; - - TypeFlag = header[offset++]; - - LinkName = ParseName(header, offset, NAMELEN, nameEncoding).ToString(); - offset += NAMELEN; - - Magic = ParseName(header, offset, MAGICLEN, nameEncoding).ToString(); - offset += MAGICLEN; - - if (Magic == "ustar") - { - Version = ParseName(header, offset, VERSIONLEN, nameEncoding).ToString(); - offset += VERSIONLEN; - - UserName = ParseName(header, offset, UNAMELEN, nameEncoding).ToString(); - offset += UNAMELEN; - - GroupName = ParseName(header, offset, GNAMELEN, nameEncoding).ToString(); - offset += GNAMELEN; - - DevMajor = (int)ParseOctal(header, offset, DEVLEN); - offset += DEVLEN; - - DevMinor = (int)ParseOctal(header, offset, DEVLEN); - offset += DEVLEN; - - string prefix = ParseName(header, offset, PREFIXLEN, nameEncoding).ToString(); - if (!string.IsNullOrEmpty(prefix)) Name = prefix + '/' + Name; - } - - isChecksumValid = Checksum == TarHeader.MakeCheckSum(header); - } - - /// - /// Parse TarHeader information from a header buffer. - /// - /// - /// The tar entry header buffer to get information from. - /// - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public void ParseBuffer(byte[] header) - { - ParseBuffer(header, null); - } - - /// - /// 'Write' header information to buffer provided, updating the check sum. - /// - /// output buffer for header information - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public void WriteHeader(byte[] outBuffer) - { - WriteHeader(outBuffer, null); - } - - /// - /// 'Write' header information to buffer provided, updating the check sum. - /// - /// output buffer for header information - /// The used for the Name field, or null for ASCII only - public void WriteHeader(byte[] outBuffer, Encoding nameEncoding) - { - if (outBuffer == null) - { - throw new ArgumentNullException(nameof(outBuffer)); - } - - int offset = 0; - - offset = GetNameBytes(Name, outBuffer, offset, NAMELEN, nameEncoding); - offset = GetOctalBytes(mode, outBuffer, offset, MODELEN); - offset = GetOctalBytes(UserId, outBuffer, offset, UIDLEN); - offset = GetOctalBytes(GroupId, outBuffer, offset, GIDLEN); - - offset = GetBinaryOrOctalBytes(Size, outBuffer, offset, SIZELEN); - offset = GetOctalBytes(GetCTime(ModTime), outBuffer, offset, MODTIMELEN); - - int csOffset = offset; - for (int c = 0; c < CHKSUMLEN; ++c) - { - outBuffer[offset++] = (byte)' '; - } - - outBuffer[offset++] = TypeFlag; - - offset = GetNameBytes(LinkName, outBuffer, offset, NAMELEN, nameEncoding); - offset = GetAsciiBytes(Magic, 0, outBuffer, offset, MAGICLEN, nameEncoding); - offset = GetNameBytes(Version, outBuffer, offset, VERSIONLEN, nameEncoding); - offset = GetNameBytes(UserName, outBuffer, offset, UNAMELEN, nameEncoding); - offset = GetNameBytes(GroupName, outBuffer, offset, GNAMELEN, nameEncoding); - - if ((TypeFlag == LF_CHR) || (TypeFlag == LF_BLK)) - { - offset = GetOctalBytes(DevMajor, outBuffer, offset, DEVLEN); - offset = GetOctalBytes(DevMinor, outBuffer, offset, DEVLEN); - } - - for (; offset < outBuffer.Length;) - { - outBuffer[offset++] = 0; - } - - checksum = ComputeCheckSum(outBuffer); - - GetCheckSumOctalBytes(checksum, outBuffer, csOffset, CHKSUMLEN); - isChecksumValid = true; - } - - /// - /// Get a hash code for the current object. - /// - /// A hash code for the current object. - public override int GetHashCode() - { - return Name.GetHashCode(); - } - - /// - /// Determines if this instance is equal to the specified object. - /// - /// The object to compare with. - /// true if the objects are equal, false otherwise. - public override bool Equals(object obj) - { - var localHeader = obj as TarHeader; - - bool result; - if (localHeader != null) - { - result = (name == localHeader.name) - && (mode == localHeader.mode) - && (UserId == localHeader.UserId) - && (GroupId == localHeader.GroupId) - && (Size == localHeader.Size) - && (ModTime == localHeader.ModTime) - && (Checksum == localHeader.Checksum) - && (TypeFlag == localHeader.TypeFlag) - && (LinkName == localHeader.LinkName) - && (Magic == localHeader.Magic) - && (Version == localHeader.Version) - && (UserName == localHeader.UserName) - && (GroupName == localHeader.GroupName) - && (DevMajor == localHeader.DevMajor) - && (DevMinor == localHeader.DevMinor); - } - else - { - result = false; - } - return result; - } - - /// - /// Set defaults for values used when constructing a TarHeader instance. - /// - /// Value to apply as a default for userId. - /// Value to apply as a default for userName. - /// Value to apply as a default for groupId. - /// Value to apply as a default for groupName. - static internal void SetValueDefaults(int userId, string userName, int groupId, string groupName) - { - defaultUserId = userIdAsSet = userId; - defaultUser = userNameAsSet = userName; - defaultGroupId = groupIdAsSet = groupId; - defaultGroupName = groupNameAsSet = groupName; - } - - static internal void RestoreSetValues() - { - defaultUserId = userIdAsSet; - defaultUser = userNameAsSet; - defaultGroupId = groupIdAsSet; - defaultGroupName = groupNameAsSet; - } - - // Return value that may be stored in octal or binary. Length must exceed 8. - // - static private long ParseBinaryOrOctal(byte[] header, int offset, int length) - { - if (header[offset] >= 0x80) - { - // File sizes over 8GB are stored in 8 right-justified bytes of binary indicated by setting the high-order bit of the leftmost byte of a numeric field. - long result = 0; - for (int pos = length - 8; pos < length; pos++) - { - result = result << 8 | header[offset + pos]; - } - return result; - } - return ParseOctal(header, offset, length); - } - - /// - /// Parse an octal string from a header buffer. - /// - /// The header buffer from which to parse. - /// The offset into the buffer from which to parse. - /// The number of header bytes to parse. - /// The long equivalent of the octal string. - static public long ParseOctal(byte[] header, int offset, int length) - { - if (header == null) - { - throw new ArgumentNullException(nameof(header)); - } - - long result = 0; - bool stillPadding = true; - - int end = offset + length; - for (int i = offset; i < end; ++i) - { - if (header[i] == 0) - { - break; - } - - if (header[i] == (byte)' ' || header[i] == '0') - { - if (stillPadding) - { - continue; - } - - if (header[i] == (byte)' ') - { - break; - } - } - - stillPadding = false; - - result = (result << 3) + (header[i] - '0'); - } - - return result; - } - - /// - /// Parse a name from a header buffer. - /// - /// - /// The header buffer from which to parse. - /// - /// - /// The offset into the buffer from which to parse. - /// - /// - /// The number of header bytes to parse. - /// - /// - /// The name parsed. - /// - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - static public StringBuilder ParseName(byte[] header, int offset, int length) - { - return ParseName(header, offset, length, null); - } - - /// - /// Parse a name from a header buffer. - /// - /// - /// The header buffer from which to parse. - /// - /// - /// The offset into the buffer from which to parse. - /// - /// - /// The number of header bytes to parse. - /// - /// - /// name encoding, or null for ASCII only - /// - /// - /// The name parsed. - /// - static public StringBuilder ParseName(byte[] header, int offset, int length, Encoding encoding) - { - if (header == null) - { - throw new ArgumentNullException(nameof(header)); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be less than zero"); - } - - if (length < 0) - { - throw new ArgumentOutOfRangeException(nameof(length), "Cannot be less than zero"); - } - - if (offset + length > header.Length) - { - throw new ArgumentException("Exceeds header size", nameof(length)); - } - - var result = new StringBuilder(length); - - int count = 0; - if(encoding == null) - { - for (int i = offset; i < offset + length; ++i) - { - if (header[i] == 0) - { - break; - } - result.Append((char)header[i]); - } - } - else - { - for(int i = offset; i < offset + length; ++i, ++count) - { - if(header[i] == 0) - { - break; - } - } - result.Append(encoding.GetString(header, offset, count)); - } - - return result; - } - - /// - /// Add name to the buffer as a collection of bytes - /// - /// The name to add - /// The offset of the first character - /// The buffer to add to - /// The index of the first byte to add - /// The number of characters/bytes to add - /// The next free index in the - public static int GetNameBytes(StringBuilder name, int nameOffset, byte[] buffer, int bufferOffset, int length) - { - return GetNameBytes(name.ToString(), nameOffset, buffer, bufferOffset, length, null); - } - - /// - /// Add name to the buffer as a collection of bytes - /// - /// The name to add - /// The offset of the first character - /// The buffer to add to - /// The index of the first byte to add - /// The number of characters/bytes to add - /// The next free index in the - public static int GetNameBytes(string name, int nameOffset, byte[] buffer, int bufferOffset, int length) - { - return GetNameBytes(name, nameOffset, buffer, bufferOffset, length, null); - } - - /// - /// Add name to the buffer as a collection of bytes - /// - /// The name to add - /// The offset of the first character - /// The buffer to add to - /// The index of the first byte to add - /// The number of characters/bytes to add - /// name encoding, or null for ASCII only - /// The next free index in the - public static int GetNameBytes(string name, int nameOffset, byte[] buffer, int bufferOffset, int length, Encoding encoding) - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - int i; - if(encoding != null) - { - // it can be more sufficient if using Span or unsafe - var nameArray = name.ToCharArray(nameOffset, Math.Min(name.Length - nameOffset, length)); - // it can be more sufficient if using Span(or unsafe?) and ArrayPool for temporary buffer - var bytes = encoding.GetBytes(nameArray, 0, nameArray.Length); - i = Math.Min(bytes.Length, length); - Array.Copy(bytes, 0, buffer, bufferOffset, i); - } - else - { - for (i = 0; i < length && nameOffset + i < name.Length; ++i) - { - buffer[bufferOffset + i] = (byte)name[nameOffset + i]; - } - } - - for (; i < length; ++i) - { - buffer[bufferOffset + i] = 0; - } - return bufferOffset + length; - } - /// - /// Add an entry name to the buffer - /// - /// - /// The name to add - /// - /// - /// The buffer to add to - /// - /// - /// The offset into the buffer from which to start adding - /// - /// - /// The number of header bytes to add - /// - /// - /// The index of the next free byte in the buffer - /// - /// TODO: what should be default behavior?(omit upper byte or UTF8?) - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public static int GetNameBytes(StringBuilder name, byte[] buffer, int offset, int length) - { - return GetNameBytes(name, buffer, offset, length, null); - } - - /// - /// Add an entry name to the buffer - /// - /// - /// The name to add - /// - /// - /// The buffer to add to - /// - /// - /// The offset into the buffer from which to start adding - /// - /// - /// The number of header bytes to add - /// - /// - /// - /// - /// The index of the next free byte in the buffer - /// - public static int GetNameBytes(StringBuilder name, byte[] buffer, int offset, int length, Encoding encoding) - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - return GetNameBytes(name.ToString(), 0, buffer, offset, length, encoding); - } - - /// - /// Add an entry name to the buffer - /// - /// The name to add - /// The buffer to add to - /// The offset into the buffer from which to start adding - /// The number of header bytes to add - /// The index of the next free byte in the buffer - /// TODO: what should be default behavior?(omit upper byte or UTF8?) - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public static int GetNameBytes(string name, byte[] buffer, int offset, int length) - { - return GetNameBytes(name, buffer, offset, length, null); - } - - /// - /// Add an entry name to the buffer - /// - /// The name to add - /// The buffer to add to - /// The offset into the buffer from which to start adding - /// The number of header bytes to add - /// - /// The index of the next free byte in the buffer - public static int GetNameBytes(string name, byte[] buffer, int offset, int length, Encoding encoding) - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - return GetNameBytes(name, 0, buffer, offset, length, encoding); - } - /// - /// Add a string to a buffer as a collection of ascii bytes. - /// - /// The string to add - /// The offset of the first character to add. - /// The buffer to add to. - /// The offset to start adding at. - /// The number of ascii characters to add. - /// The next free index in the buffer. - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public static int GetAsciiBytes(string toAdd, int nameOffset, byte[] buffer, int bufferOffset, int length) - { - return GetAsciiBytes(toAdd, nameOffset, buffer, bufferOffset, length, null); - } - - /// - /// Add a string to a buffer as a collection of ascii bytes. - /// - /// The string to add - /// The offset of the first character to add. - /// The buffer to add to. - /// The offset to start adding at. - /// The number of ascii characters to add. - /// String encoding, or null for ASCII only - /// The next free index in the buffer. - public static int GetAsciiBytes(string toAdd, int nameOffset, byte[] buffer, int bufferOffset, int length, Encoding encoding) - { - if (toAdd == null) - { - throw new ArgumentNullException(nameof(toAdd)); - } - - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - int i; - if(encoding == null) - { - for (i = 0; i < length && nameOffset + i < toAdd.Length; ++i) - { - buffer[bufferOffset + i] = (byte)toAdd[nameOffset + i]; - } - } - else - { - // It can be more sufficient if using unsafe code or Span(ToCharArray can be omitted) - var chars = toAdd.ToCharArray(); - // It can be more sufficient if using Span(or unsafe?) and ArrayPool for temporary buffer - var bytes = encoding.GetBytes(chars, nameOffset, Math.Min(toAdd.Length - nameOffset, length)); - i = Math.Min(bytes.Length, length); - Array.Copy(bytes, 0, buffer, bufferOffset, i); - } - // If length is beyond the toAdd string length (which is OK by the prev loop condition), eg if a field has fixed length and the string is shorter, make sure all of the extra chars are written as NULLs, so that the reader func would ignore them and get back the original string - for (; i < length; ++i) - buffer[bufferOffset + i] = 0; - return bufferOffset + length; - } - - /// - /// Put an octal representation of a value into a buffer - /// - /// - /// the value to be converted to octal - /// - /// - /// buffer to store the octal string - /// - /// - /// The offset into the buffer where the value starts - /// - /// - /// The length of the octal string to create - /// - /// - /// The offset of the character next byte after the octal string - /// - public static int GetOctalBytes(long value, byte[] buffer, int offset, int length) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - int localIndex = length - 1; - - // Either a space or null is valid here. We use NULL as per GNUTar - buffer[offset + localIndex] = 0; - --localIndex; - - if (value > 0) - { - for (long v = value; (localIndex >= 0) && (v > 0); --localIndex) - { - buffer[offset + localIndex] = (byte)((byte)'0' + (byte)(v & 7)); - v >>= 3; - } - } - - for (; localIndex >= 0; --localIndex) - { - buffer[offset + localIndex] = (byte)'0'; - } - - return offset + length; - } - - /// - /// Put an octal or binary representation of a value into a buffer - /// - /// Value to be convert to octal - /// The buffer to update - /// The offset into the buffer to store the value - /// The length of the octal string. Must be 12. - /// Index of next byte - private static int GetBinaryOrOctalBytes(long value, byte[] buffer, int offset, int length) - { - if (value > 0x1FFFFFFFF) - { // Octal 77777777777 (11 digits) - // Put value as binary, right-justified into the buffer. Set high order bit of left-most byte. - for (int pos = length - 1; pos > 0; pos--) - { - buffer[offset + pos] = (byte)value; - value = value >> 8; - } - buffer[offset] = 0x80; - return offset + length; - } - return GetOctalBytes(value, buffer, offset, length); - } - - /// - /// Add the checksum integer to header buffer. - /// - /// - /// The header buffer to set the checksum for - /// The offset into the buffer for the checksum - /// The number of header bytes to update. - /// It's formatted differently from the other fields: it has 6 digits, a - /// null, then a space -- rather than digits, a space, then a null. - /// The final space is already there, from checksumming - /// - /// The modified buffer offset - private static void GetCheckSumOctalBytes(long value, byte[] buffer, int offset, int length) - { - GetOctalBytes(value, buffer, offset, length - 1); - } - - /// - /// Compute the checksum for a tar entry header. - /// The checksum field must be all spaces prior to this happening - /// - /// The tar entry's header buffer. - /// The computed checksum. - private static int ComputeCheckSum(byte[] buffer) - { - int sum = 0; - for (int i = 0; i < buffer.Length; ++i) - { - sum += buffer[i]; - } - return sum; - } - - /// - /// Make a checksum for a tar entry ignoring the checksum contents. - /// - /// The tar entry's header buffer. - /// The checksum for the buffer - private static int MakeCheckSum(byte[] buffer) - { - int sum = 0; - for (int i = 0; i < CHKSUMOFS; ++i) - { - sum += buffer[i]; - } - - for (int i = 0; i < CHKSUMLEN; ++i) - { - sum += (byte)' '; - } - - for (int i = CHKSUMOFS + CHKSUMLEN; i < buffer.Length; ++i) - { - sum += buffer[i]; - } - return sum; - } - - private static int GetCTime(DateTime dateTime) - { - return unchecked((int)((dateTime.Ticks - dateTime1970.Ticks) / timeConversionFactor)); - } - - private static DateTime GetDateTimeFromCTime(long ticks) - { - DateTime result; - - try - { - result = new DateTime(dateTime1970.Ticks + ticks * timeConversionFactor); - } - catch (ArgumentOutOfRangeException) - { - result = dateTime1970; - } - return result; - } - - #region Instance Fields - - private string name; - private int mode; - private int userId; - private int groupId; - private long size; - private DateTime modTime; - private int checksum; - private bool isChecksumValid; - private byte typeFlag; - private string linkName; - private string magic; - private string version; - private string userName; - private string groupName; - private int devMajor; - private int devMinor; - - #endregion Instance Fields - - #region Class Fields - - // Values used during recursive operations. - static internal int userIdAsSet; - - static internal int groupIdAsSet; - static internal string userNameAsSet; - static internal string groupNameAsSet = "None"; - - static internal int defaultUserId; - static internal int defaultGroupId; - static internal string defaultGroupName = "None"; - static internal string defaultUser; - - #endregion Class Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs b/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs deleted file mode 100644 index f1a3622debcb..000000000000 --- a/ICSharpCode.SharpZipLib/Tar/TarInputStream.cs +++ /dev/null @@ -1,771 +0,0 @@ -using System; -using System.IO; -using System.Text; - -namespace ICSharpCode.SharpZipLib.Tar -{ - /// - /// The TarInputStream reads a UNIX tar archive as an InputStream. - /// methods are provided to position at each successive entry in - /// the archive, and the read each entry as a normal input stream - /// using read(). - /// - public class TarInputStream : Stream - { - #region Constructors - - /// - /// Construct a TarInputStream with default block factor - /// - /// stream to source data from - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public TarInputStream(Stream inputStream) - : this(inputStream, TarBuffer.DefaultBlockFactor, null) - { - } - /// - /// Construct a TarInputStream with default block factor - /// - /// stream to source data from - /// The used for the Name fields, or null for ASCII only - public TarInputStream(Stream inputStream, Encoding nameEncoding) - : this(inputStream, TarBuffer.DefaultBlockFactor, nameEncoding) - { - } - - /// - /// Construct a TarInputStream with user specified block factor - /// - /// stream to source data from - /// block factor to apply to archive - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public TarInputStream(Stream inputStream, int blockFactor) - { - this.inputStream = inputStream; - tarBuffer = TarBuffer.CreateInputTarBuffer(inputStream, blockFactor); - encoding = null; - } - - /// - /// Construct a TarInputStream with user specified block factor - /// - /// stream to source data from - /// block factor to apply to archive - /// The used for the Name fields, or null for ASCII only - public TarInputStream(Stream inputStream, int blockFactor, Encoding nameEncoding) - { - this.inputStream = inputStream; - tarBuffer = TarBuffer.CreateInputTarBuffer(inputStream, blockFactor); - encoding = nameEncoding; - } - - #endregion Constructors - - /// - /// Gets or sets a flag indicating ownership of underlying stream. - /// When the flag is true will close the underlying stream also. - /// - /// The default value is true. - public bool IsStreamOwner - { - get { return tarBuffer.IsStreamOwner; } - set { tarBuffer.IsStreamOwner = value; } - } - - #region Stream Overrides - - /// - /// Gets a value indicating whether the current stream supports reading - /// - public override bool CanRead - { - get - { - return inputStream.CanRead; - } - } - - /// - /// Gets a value indicating whether the current stream supports seeking - /// This property always returns false. - /// - public override bool CanSeek - { - get - { - return false; - } - } - - /// - /// Gets a value indicating if the stream supports writing. - /// This property always returns false. - /// - public override bool CanWrite - { - get - { - return false; - } - } - - /// - /// The length in bytes of the stream - /// - public override long Length - { - get - { - return inputStream.Length; - } - } - - /// - /// Gets or sets the position within the stream. - /// Setting the Position is not supported and throws a NotSupportedExceptionNotSupportedException - /// - /// Any attempt to set position - public override long Position - { - get - { - return inputStream.Position; - } - set - { - throw new NotSupportedException("TarInputStream Seek not supported"); - } - } - - /// - /// Flushes the baseInputStream - /// - public override void Flush() - { - inputStream.Flush(); - } - - /// - /// Set the streams position. This operation is not supported and will throw a NotSupportedException - /// - /// The offset relative to the origin to seek to. - /// The to start seeking from. - /// The new position in the stream. - /// Any access - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException("TarInputStream Seek not supported"); - } - - /// - /// Sets the length of the stream - /// This operation is not supported and will throw a NotSupportedException - /// - /// The new stream length. - /// Any access - public override void SetLength(long value) - { - throw new NotSupportedException("TarInputStream SetLength not supported"); - } - - /// - /// Writes a block of bytes to this stream using data from a buffer. - /// This operation is not supported and will throw a NotSupportedException - /// - /// The buffer containing bytes to write. - /// The offset in the buffer of the frist byte to write. - /// The number of bytes to write. - /// Any access - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException("TarInputStream Write not supported"); - } - - /// - /// Writes a byte to the current position in the file stream. - /// This operation is not supported and will throw a NotSupportedException - /// - /// The byte value to write. - /// Any access - public override void WriteByte(byte value) - { - throw new NotSupportedException("TarInputStream WriteByte not supported"); - } - - /// - /// Reads a byte from the current tar archive entry. - /// - /// A byte cast to an int; -1 if the at the end of the stream. - public override int ReadByte() - { - byte[] oneByteBuffer = new byte[1]; - int num = Read(oneByteBuffer, 0, 1); - if (num <= 0) - { - // return -1 to indicate that no byte was read. - return -1; - } - return oneByteBuffer[0]; - } - - /// - /// Reads bytes from the current tar archive entry. - /// - /// This method is aware of the boundaries of the current - /// entry in the archive and will deal with them appropriately - /// - /// - /// The buffer into which to place bytes read. - /// - /// - /// The offset at which to place bytes read. - /// - /// - /// The number of bytes to read. - /// - /// - /// The number of bytes read, or 0 at end of stream/EOF. - /// - public override int Read(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - int totalRead = 0; - - if (entryOffset >= entrySize) - { - return 0; - } - - long numToRead = count; - - if ((numToRead + entryOffset) > entrySize) - { - numToRead = entrySize - entryOffset; - } - - if (readBuffer != null) - { - int sz = (numToRead > readBuffer.Length) ? readBuffer.Length : (int)numToRead; - - Array.Copy(readBuffer, 0, buffer, offset, sz); - - if (sz >= readBuffer.Length) - { - readBuffer = null; - } - else - { - int newLen = readBuffer.Length - sz; - byte[] newBuf = new byte[newLen]; - Array.Copy(readBuffer, sz, newBuf, 0, newLen); - readBuffer = newBuf; - } - - totalRead += sz; - numToRead -= sz; - offset += sz; - } - - while (numToRead > 0) - { - byte[] rec = tarBuffer.ReadBlock(); - if (rec == null) - { - // Unexpected EOF! - throw new TarException("unexpected EOF with " + numToRead + " bytes unread"); - } - - var sz = (int)numToRead; - int recLen = rec.Length; - - if (recLen > sz) - { - Array.Copy(rec, 0, buffer, offset, sz); - readBuffer = new byte[recLen - sz]; - Array.Copy(rec, sz, readBuffer, 0, recLen - sz); - } - else - { - sz = recLen; - Array.Copy(rec, 0, buffer, offset, recLen); - } - - totalRead += sz; - numToRead -= sz; - offset += sz; - } - - entryOffset += totalRead; - - return totalRead; - } - - /// - /// Closes this stream. Calls the TarBuffer's close() method. - /// The underlying stream is closed by the TarBuffer. - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - tarBuffer.Close(); - } - } - - #endregion Stream Overrides - - /// - /// Set the entry factory for this instance. - /// - /// The factory for creating new entries - public void SetEntryFactory(IEntryFactory factory) - { - entryFactory = factory; - } - - /// - /// Get the record size being used by this stream's TarBuffer. - /// - public int RecordSize - { - get { return tarBuffer.RecordSize; } - } - - /// - /// Get the record size being used by this stream's TarBuffer. - /// - /// - /// TarBuffer record size. - /// - [Obsolete("Use RecordSize property instead")] - public int GetRecordSize() - { - return tarBuffer.RecordSize; - } - - /// - /// Get the available data that can be read from the current - /// entry in the archive. This does not indicate how much data - /// is left in the entire archive, only in the current entry. - /// This value is determined from the entry's size header field - /// and the amount of data already read from the current entry. - /// - /// - /// The number of available bytes for the current entry. - /// - public long Available - { - get - { - return entrySize - entryOffset; - } - } - - /// - /// Skip bytes in the input buffer. This skips bytes in the - /// current entry's data, not the entire archive, and will - /// stop at the end of the current entry's data if the number - /// to skip extends beyond that point. - /// - /// - /// The number of bytes to skip. - /// - public void Skip(long skipCount) - { - // TODO: REVIEW efficiency of TarInputStream.Skip - // This is horribly inefficient, but it ensures that we - // properly skip over bytes via the TarBuffer... - // - byte[] skipBuf = new byte[8 * 1024]; - - for (long num = skipCount; num > 0;) - { - int toRead = num > skipBuf.Length ? skipBuf.Length : (int)num; - int numRead = Read(skipBuf, 0, toRead); - - if (numRead == -1) - { - break; - } - - num -= numRead; - } - } - - /// - /// Return a value of true if marking is supported; false otherwise. - /// - /// Currently marking is not supported, the return value is always false. - public bool IsMarkSupported - { - get - { - return false; - } - } - - /// - /// Since we do not support marking just yet, we do nothing. - /// - /// - /// The limit to mark. - /// - public void Mark(int markLimit) - { - } - - /// - /// Since we do not support marking just yet, we do nothing. - /// - public void Reset() - { - } - - /// - /// Get the next entry in this tar archive. This will skip - /// over any remaining data in the current entry, if there - /// is one, and place the input stream at the header of the - /// next entry, and read the header and instantiate a new - /// TarEntry from the header bytes and return that entry. - /// If there are no more entries in the archive, null will - /// be returned to indicate that the end of the archive has - /// been reached. - /// - /// - /// The next TarEntry in the archive, or null. - /// - public TarEntry GetNextEntry() - { - if (hasHitEOF) - { - return null; - } - - if (currentEntry != null) - { - SkipToNextEntry(); - } - - byte[] headerBuf = tarBuffer.ReadBlock(); - - if (headerBuf == null) - { - hasHitEOF = true; - } - else if (TarBuffer.IsEndOfArchiveBlock(headerBuf)) - { - hasHitEOF = true; - - // Read the second zero-filled block - tarBuffer.ReadBlock(); - } - else - { - hasHitEOF = false; - } - - if (hasHitEOF) - { - currentEntry = null; - } - else - { - try - { - var header = new TarHeader(); - header.ParseBuffer(headerBuf, encoding); - if (!header.IsChecksumValid) - { - throw new TarException("Header checksum is invalid"); - } - this.entryOffset = 0; - this.entrySize = header.Size; - - StringBuilder longName = null; - - if (header.TypeFlag == TarHeader.LF_GNU_LONGNAME) - { - byte[] nameBuffer = new byte[TarBuffer.BlockSize]; - long numToRead = this.entrySize; - - longName = new StringBuilder(); - - while (numToRead > 0) - { - int numRead = this.Read(nameBuffer, 0, (numToRead > nameBuffer.Length ? nameBuffer.Length : (int)numToRead)); - - if (numRead == -1) - { - throw new InvalidHeaderException("Failed to read long name entry"); - } - - longName.Append(TarHeader.ParseName(nameBuffer, 0, numRead, encoding).ToString()); - numToRead -= numRead; - } - - SkipToNextEntry(); - headerBuf = this.tarBuffer.ReadBlock(); - } - else if (header.TypeFlag == TarHeader.LF_GHDR) - { // POSIX global extended header - // Ignore things we dont understand completely for now - SkipToNextEntry(); - headerBuf = this.tarBuffer.ReadBlock(); - } - else if (header.TypeFlag == TarHeader.LF_XHDR) - { // POSIX extended header - byte[] nameBuffer = new byte[TarBuffer.BlockSize]; - long numToRead = this.entrySize; - - var xhr = new TarExtendedHeaderReader(); - - while (numToRead > 0) - { - int numRead = this.Read(nameBuffer, 0, (numToRead > nameBuffer.Length ? nameBuffer.Length : (int)numToRead)); - - if (numRead == -1) - { - throw new InvalidHeaderException("Failed to read long name entry"); - } - - xhr.Read(nameBuffer, numRead); - numToRead -= numRead; - } - - if (xhr.Headers.TryGetValue("path", out string name)) - { - longName = new StringBuilder(name); - } - - SkipToNextEntry(); - headerBuf = this.tarBuffer.ReadBlock(); - } - else if (header.TypeFlag == TarHeader.LF_GNU_VOLHDR) - { - // TODO: could show volume name when verbose - SkipToNextEntry(); - headerBuf = this.tarBuffer.ReadBlock(); - } - else if (header.TypeFlag != TarHeader.LF_NORMAL && - header.TypeFlag != TarHeader.LF_OLDNORM && - header.TypeFlag != TarHeader.LF_LINK && - header.TypeFlag != TarHeader.LF_SYMLINK && - header.TypeFlag != TarHeader.LF_DIR) - { - // Ignore things we dont understand completely for now - SkipToNextEntry(); - headerBuf = tarBuffer.ReadBlock(); - } - - if (entryFactory == null) - { - currentEntry = new TarEntry(headerBuf, encoding); - if (longName != null) - { - currentEntry.Name = longName.ToString(); - } - } - else - { - currentEntry = entryFactory.CreateEntry(headerBuf); - } - - // Magic was checked here for 'ustar' but there are multiple valid possibilities - // so this is not done anymore. - - entryOffset = 0; - - // TODO: Review How do we resolve this discrepancy?! - entrySize = this.currentEntry.Size; - } - catch (InvalidHeaderException ex) - { - entrySize = 0; - entryOffset = 0; - currentEntry = null; - string errorText = string.Format("Bad header in record {0} block {1} {2}", - tarBuffer.CurrentRecord, tarBuffer.CurrentBlock, ex.Message); - throw new InvalidHeaderException(errorText); - } - } - return currentEntry; - } - - /// - /// Copies the contents of the current tar archive entry directly into - /// an output stream. - /// - /// - /// The OutputStream into which to write the entry's data. - /// - public void CopyEntryContents(Stream outputStream) - { - byte[] tempBuffer = new byte[32 * 1024]; - - while (true) - { - int numRead = Read(tempBuffer, 0, tempBuffer.Length); - if (numRead <= 0) - { - break; - } - outputStream.Write(tempBuffer, 0, numRead); - } - } - - private void SkipToNextEntry() - { - long numToSkip = entrySize - entryOffset; - - if (numToSkip > 0) - { - Skip(numToSkip); - } - - readBuffer = null; - } - - /// - /// This interface is provided, along with the method , to allow - /// the programmer to have their own subclass instantiated for the - /// entries return from . - /// - public interface IEntryFactory - { - // This interface does not considering name encoding. - // How this interface should be? - /// - /// Create an entry based on name alone - /// - /// - /// Name of the new EntryPointNotFoundException to create - /// - /// created TarEntry or descendant class - TarEntry CreateEntry(string name); - - /// - /// Create an instance based on an actual file - /// - /// - /// Name of file to represent in the entry - /// - /// - /// Created TarEntry or descendant class - /// - TarEntry CreateEntryFromFile(string fileName); - - /// - /// Create a tar entry based on the header information passed - /// - /// - /// Buffer containing header information to create an entry from. - /// - /// - /// Created TarEntry or descendant class - /// - TarEntry CreateEntry(byte[] headerBuffer); - } - - /// - /// Standard entry factory class creating instances of the class TarEntry - /// - public class EntryFactoryAdapter : IEntryFactory - { - Encoding nameEncoding; - /// - /// Construct standard entry factory class with ASCII name encoding - /// - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public EntryFactoryAdapter() - { - } - /// - /// Construct standard entry factory with name encoding - /// - /// The used for the Name fields, or null for ASCII only - public EntryFactoryAdapter(Encoding nameEncoding) - { - this.nameEncoding = nameEncoding; - } - /// - /// Create a based on named - /// - /// The name to use for the entry - /// A new - public TarEntry CreateEntry(string name) - { - return TarEntry.CreateTarEntry(name); - } - - /// - /// Create a tar entry with details obtained from file - /// - /// The name of the file to retrieve details from. - /// A new - public TarEntry CreateEntryFromFile(string fileName) - { - return TarEntry.CreateEntryFromFile(fileName); - } - - /// - /// Create an entry based on details in header - /// - /// The buffer containing entry details. - /// A new - public TarEntry CreateEntry(byte[] headerBuffer) - { - return new TarEntry(headerBuffer, nameEncoding); - } - } - - #region Instance Fields - - /// - /// Flag set when last block has been read - /// - protected bool hasHitEOF; - - /// - /// Size of this entry as recorded in header - /// - protected long entrySize; - - /// - /// Number of bytes read for this entry so far - /// - protected long entryOffset; - - /// - /// Buffer used with calls to Read() - /// - protected byte[] readBuffer; - - /// - /// Working buffer - /// - protected TarBuffer tarBuffer; - - /// - /// Current entry being read - /// - private TarEntry currentEntry; - - /// - /// Factory used to create TarEntry or descendant class instance - /// - protected IEntryFactory entryFactory; - - /// - /// Stream used as the source of input data. - /// - private readonly Stream inputStream; - - private readonly Encoding encoding; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Tar/TarOutputStream.cs b/ICSharpCode.SharpZipLib/Tar/TarOutputStream.cs deleted file mode 100644 index 7c52e6c7c092..000000000000 --- a/ICSharpCode.SharpZipLib/Tar/TarOutputStream.cs +++ /dev/null @@ -1,522 +0,0 @@ -using System; -using System.IO; -using System.Text; - -namespace ICSharpCode.SharpZipLib.Tar -{ - /// - /// The TarOutputStream writes a UNIX tar archive as an OutputStream. - /// Methods are provided to put entries, and then write their contents - /// by writing to this stream using write(). - /// - /// public - public class TarOutputStream : Stream - { - #region Constructors - - /// - /// Construct TarOutputStream using default block factor - /// - /// stream to write to - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public TarOutputStream(Stream outputStream) - : this(outputStream, TarBuffer.DefaultBlockFactor) - { - } - - /// - /// Construct TarOutputStream using default block factor - /// - /// stream to write to - /// The used for the Name fields, or null for ASCII only - public TarOutputStream(Stream outputStream, Encoding nameEncoding) - : this(outputStream, TarBuffer.DefaultBlockFactor, nameEncoding) - { - } - - /// - /// Construct TarOutputStream with user specified block factor - /// - /// stream to write to - /// blocking factor - [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] - public TarOutputStream(Stream outputStream, int blockFactor) - { - if (outputStream == null) - { - throw new ArgumentNullException(nameof(outputStream)); - } - - this.outputStream = outputStream; - buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor); - - assemblyBuffer = new byte[TarBuffer.BlockSize]; - blockBuffer = new byte[TarBuffer.BlockSize]; - } - - /// - /// Construct TarOutputStream with user specified block factor - /// - /// stream to write to - /// blocking factor - /// The used for the Name fields, or null for ASCII only - public TarOutputStream(Stream outputStream, int blockFactor, Encoding nameEncoding) - { - if (outputStream == null) - { - throw new ArgumentNullException(nameof(outputStream)); - } - - this.outputStream = outputStream; - buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor); - - assemblyBuffer = new byte[TarBuffer.BlockSize]; - blockBuffer = new byte[TarBuffer.BlockSize]; - - this.nameEncoding = nameEncoding; - } - - #endregion Constructors - - /// - /// Gets or sets a flag indicating ownership of underlying stream. - /// When the flag is true will close the underlying stream also. - /// - /// The default value is true. - public bool IsStreamOwner - { - get { return buffer.IsStreamOwner; } - set { buffer.IsStreamOwner = value; } - } - - /// - /// true if the stream supports reading; otherwise, false. - /// - public override bool CanRead - { - get - { - return outputStream.CanRead; - } - } - - /// - /// true if the stream supports seeking; otherwise, false. - /// - public override bool CanSeek - { - get - { - return outputStream.CanSeek; - } - } - - /// - /// true if stream supports writing; otherwise, false. - /// - public override bool CanWrite - { - get - { - return outputStream.CanWrite; - } - } - - /// - /// length of stream in bytes - /// - public override long Length - { - get - { - return outputStream.Length; - } - } - - /// - /// gets or sets the position within the current stream. - /// - public override long Position - { - get - { - return outputStream.Position; - } - set - { - outputStream.Position = value; - } - } - - /// - /// set the position within the current stream - /// - /// The offset relative to the to seek to - /// The to seek from. - /// The new position in the stream. - public override long Seek(long offset, SeekOrigin origin) - { - return outputStream.Seek(offset, origin); - } - - /// - /// Set the length of the current stream - /// - /// The new stream length. - public override void SetLength(long value) - { - outputStream.SetLength(value); - } - - /// - /// Read a byte from the stream and advance the position within the stream - /// by one byte or returns -1 if at the end of the stream. - /// - /// The byte value or -1 if at end of stream - public override int ReadByte() - { - return outputStream.ReadByte(); - } - - /// - /// read bytes from the current stream and advance the position within the - /// stream by the number of bytes read. - /// - /// The buffer to store read bytes in. - /// The index into the buffer to being storing bytes at. - /// The desired number of bytes to read. - /// The total number of bytes read, or zero if at the end of the stream. - /// The number of bytes may be less than the count - /// requested if data is not available. - public override int Read(byte[] buffer, int offset, int count) - { - return outputStream.Read(buffer, offset, count); - } - - /// - /// All buffered data is written to destination - /// - public override void Flush() - { - outputStream.Flush(); - } - - /// - /// Ends the TAR archive without closing the underlying OutputStream. - /// The result is that the EOF block of nulls is written. - /// - public void Finish() - { - if (IsEntryOpen) - { - CloseEntry(); - } - WriteEofBlock(); - } - - /// - /// Ends the TAR archive and closes the underlying OutputStream. - /// - /// This means that Finish() is called followed by calling the - /// TarBuffer's Close(). - protected override void Dispose(bool disposing) - { - if (!isClosed) - { - isClosed = true; - Finish(); - buffer.Close(); - } - } - - /// - /// Get the record size being used by this stream's TarBuffer. - /// - public int RecordSize - { - get { return buffer.RecordSize; } - } - - /// - /// Get the record size being used by this stream's TarBuffer. - /// - /// - /// The TarBuffer record size. - /// - [Obsolete("Use RecordSize property instead")] - public int GetRecordSize() - { - return buffer.RecordSize; - } - - /// - /// Get a value indicating whether an entry is open, requiring more data to be written. - /// - private bool IsEntryOpen - { - get { return (currBytes < currSize); } - } - - /// - /// Put an entry on the output stream. This writes the entry's - /// header and positions the output stream for writing - /// the contents of the entry. Once this method is called, the - /// stream is ready for calls to write() to write the entry's - /// contents. Once the contents are written, closeEntry() - /// MUST be called to ensure that all buffered data - /// is completely written to the output stream. - /// - /// - /// The TarEntry to be written to the archive. - /// - public void PutNextEntry(TarEntry entry) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - var namelen = nameEncoding != null ? nameEncoding.GetByteCount(entry.TarHeader.Name) : entry.TarHeader.Name.Length; - - if (namelen > TarHeader.NAMELEN) - { - var longHeader = new TarHeader(); - longHeader.TypeFlag = TarHeader.LF_GNU_LONGNAME; - longHeader.Name = longHeader.Name + "././@LongLink"; - longHeader.Mode = 420;//644 by default - longHeader.UserId = entry.UserId; - longHeader.GroupId = entry.GroupId; - longHeader.GroupName = entry.GroupName; - longHeader.UserName = entry.UserName; - longHeader.LinkName = ""; - longHeader.Size = namelen + 1; // Plus one to avoid dropping last char - - longHeader.WriteHeader(blockBuffer, nameEncoding); - buffer.WriteBlock(blockBuffer); // Add special long filename header block - - int nameCharIndex = 0; - - while (nameCharIndex < namelen + 1 /* we've allocated one for the null char, now we must make sure it gets written out */) - { - Array.Clear(blockBuffer, 0, blockBuffer.Length); - TarHeader.GetAsciiBytes(entry.TarHeader.Name, nameCharIndex, this.blockBuffer, 0, TarBuffer.BlockSize, nameEncoding); // This func handles OK the extra char out of string length - nameCharIndex += TarBuffer.BlockSize; - buffer.WriteBlock(blockBuffer); - } - } - - entry.WriteEntryHeader(blockBuffer, nameEncoding); - buffer.WriteBlock(blockBuffer); - - currBytes = 0; - - currSize = entry.IsDirectory ? 0 : entry.Size; - } - - /// - /// Close an entry. This method MUST be called for all file - /// entries that contain data. The reason is that we must - /// buffer data written to the stream in order to satisfy - /// the buffer's block based writes. Thus, there may be - /// data fragments still being assembled that must be written - /// to the output stream before this entry is closed and the - /// next entry written. - /// - public void CloseEntry() - { - if (assemblyBufferLength > 0) - { - Array.Clear(assemblyBuffer, assemblyBufferLength, assemblyBuffer.Length - assemblyBufferLength); - - buffer.WriteBlock(assemblyBuffer); - - currBytes += assemblyBufferLength; - assemblyBufferLength = 0; - } - - if (currBytes < currSize) - { - string errorText = string.Format( - "Entry closed at '{0}' before the '{1}' bytes specified in the header were written", - currBytes, currSize); - throw new TarException(errorText); - } - } - - /// - /// Writes a byte to the current tar archive entry. - /// This method simply calls Write(byte[], int, int). - /// - /// - /// The byte to be written. - /// - public override void WriteByte(byte value) - { - Write(new byte[] { value }, 0, 1); - } - - /// - /// Writes bytes to the current tar archive entry. This method - /// is aware of the current entry and will throw an exception if - /// you attempt to write bytes past the length specified for the - /// current entry. The method is also (painfully) aware of the - /// record buffering required by TarBuffer, and manages buffers - /// that are not a multiple of recordsize in length, including - /// assembling records from small buffers. - /// - /// - /// The buffer to write to the archive. - /// - /// - /// The offset in the buffer from which to get bytes. - /// - /// - /// The number of bytes to write. - /// - public override void Write(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative"); - } - - if (buffer.Length - offset < count) - { - throw new ArgumentException("offset and count combination is invalid"); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative"); - } - - if ((currBytes + count) > currSize) - { - string errorText = string.Format("request to write '{0}' bytes exceeds size in header of '{1}' bytes", - count, this.currSize); - throw new ArgumentOutOfRangeException(nameof(count), errorText); - } - - // - // We have to deal with assembly!!! - // The programmer can be writing little 32 byte chunks for all - // we know, and we must assemble complete blocks for writing. - // TODO REVIEW Maybe this should be in TarBuffer? Could that help to - // eliminate some of the buffer copying. - // - if (assemblyBufferLength > 0) - { - if ((assemblyBufferLength + count) >= blockBuffer.Length) - { - int aLen = blockBuffer.Length - assemblyBufferLength; - - Array.Copy(assemblyBuffer, 0, blockBuffer, 0, assemblyBufferLength); - Array.Copy(buffer, offset, blockBuffer, assemblyBufferLength, aLen); - - this.buffer.WriteBlock(blockBuffer); - - currBytes += blockBuffer.Length; - - offset += aLen; - count -= aLen; - - assemblyBufferLength = 0; - } - else - { - Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count); - offset += count; - assemblyBufferLength += count; - count -= count; - } - } - - // - // When we get here we have EITHER: - // o An empty "assembly" buffer. - // o No bytes to write (count == 0) - // - while (count > 0) - { - if (count < blockBuffer.Length) - { - Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count); - assemblyBufferLength += count; - break; - } - - this.buffer.WriteBlock(buffer, offset); - - int bufferLength = blockBuffer.Length; - currBytes += bufferLength; - count -= bufferLength; - offset += bufferLength; - } - } - - /// - /// Write an EOF (end of archive) block to the tar archive. - /// The end of the archive is indicated by two blocks consisting entirely of zero bytes. - /// - private void WriteEofBlock() - { - Array.Clear(blockBuffer, 0, blockBuffer.Length); - buffer.WriteBlock(blockBuffer); - buffer.WriteBlock(blockBuffer); - } - - #region Instance Fields - - /// - /// bytes written for this entry so far - /// - private long currBytes; - - /// - /// current 'Assembly' buffer length - /// - private int assemblyBufferLength; - - /// - /// Flag indicating whether this instance has been closed or not. - /// - private bool isClosed; - - /// - /// Size for the current entry - /// - protected long currSize; - - /// - /// single block working buffer - /// - protected byte[] blockBuffer; - - /// - /// 'Assembly' buffer used to assemble data before writing - /// - protected byte[] assemblyBuffer; - - /// - /// TarBuffer used to provide correct blocking factor - /// - protected TarBuffer buffer; - - /// - /// the destination stream for the archive contents - /// - protected Stream outputStream; - - /// - /// name encoding - /// - protected Encoding nameEncoding; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Deflater.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Deflater.cs deleted file mode 100644 index 3dbe98c8d9bd..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/Deflater.cs +++ /dev/null @@ -1,604 +0,0 @@ -using System; - -namespace ICSharpCode.SharpZipLib.Zip.Compression -{ - /// - /// This is the Deflater class. The deflater class compresses input - /// with the deflate algorithm described in RFC 1951. It has several - /// compression levels and three different strategies described below. - /// - /// This class is not thread safe. This is inherent in the API, due - /// to the split of deflate and setInput. - /// - /// author of the original java version : Jochen Hoenicke - /// - public class Deflater - { - #region Deflater Documentation - - /* - * The Deflater can do the following state transitions: - * - * (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---. - * / | (2) (5) | - * / v (5) | - * (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3) - * \ | (3) | ,--------' - * | | | (3) / - * v v (5) v v - * (1) -> BUSY_STATE ----> FINISHING_STATE - * | (6) - * v - * FINISHED_STATE - * \_____________________________________/ - * | (7) - * v - * CLOSED_STATE - * - * (1) If we should produce a header we start in INIT_STATE, otherwise - * we start in BUSY_STATE. - * (2) A dictionary may be set only when we are in INIT_STATE, then - * we change the state as indicated. - * (3) Whether a dictionary is set or not, on the first call of deflate - * we change to BUSY_STATE. - * (4) -- intentionally left blank -- :) - * (5) FINISHING_STATE is entered, when flush() is called to indicate that - * there is no more INPUT. There are also states indicating, that - * the header wasn't written yet. - * (6) FINISHED_STATE is entered, when everything has been flushed to the - * internal pending output buffer. - * (7) At any time (7) - * - */ - - #endregion Deflater Documentation - - #region Public Constants - - /// - /// The best and slowest compression level. This tries to find very - /// long and distant string repetitions. - /// - public const int BEST_COMPRESSION = 9; - - /// - /// The worst but fastest compression level. - /// - public const int BEST_SPEED = 1; - - /// - /// The default compression level. - /// - public const int DEFAULT_COMPRESSION = -1; - - /// - /// This level won't compress at all but output uncompressed blocks. - /// - public const int NO_COMPRESSION = 0; - - /// - /// The compression method. This is the only method supported so far. - /// There is no need to use this constant at all. - /// - public const int DEFLATED = 8; - - #endregion Public Constants - - #region Public Enum - - /// - /// Compression Level as an enum for safer use - /// - public enum CompressionLevel - { - /// - /// The best and slowest compression level. This tries to find very - /// long and distant string repetitions. - /// - BEST_COMPRESSION = Deflater.BEST_COMPRESSION, - - /// - /// The worst but fastest compression level. - /// - BEST_SPEED = Deflater.BEST_SPEED, - - /// - /// The default compression level. - /// - DEFAULT_COMPRESSION = Deflater.DEFAULT_COMPRESSION, - - /// - /// This level won't compress at all but output uncompressed blocks. - /// - NO_COMPRESSION = Deflater.NO_COMPRESSION, - - /// - /// The compression method. This is the only method supported so far. - /// There is no need to use this constant at all. - /// - DEFLATED = Deflater.DEFLATED - } - - #endregion Public Enum - - #region Local Constants - - private const int IS_SETDICT = 0x01; - private const int IS_FLUSHING = 0x04; - private const int IS_FINISHING = 0x08; - - private const int INIT_STATE = 0x00; - private const int SETDICT_STATE = 0x01; - - // private static int INIT_FINISHING_STATE = 0x08; - // private static int SETDICT_FINISHING_STATE = 0x09; - private const int BUSY_STATE = 0x10; - - private const int FLUSHING_STATE = 0x14; - private const int FINISHING_STATE = 0x1c; - private const int FINISHED_STATE = 0x1e; - private const int CLOSED_STATE = 0x7f; - - #endregion Local Constants - - #region Constructors - - /// - /// Creates a new deflater with default compression level. - /// - public Deflater() : this(DEFAULT_COMPRESSION, false) - { - } - - /// - /// Creates a new deflater with given compression level. - /// - /// - /// the compression level, a value between NO_COMPRESSION - /// and BEST_COMPRESSION, or DEFAULT_COMPRESSION. - /// - /// if lvl is out of range. - public Deflater(int level) : this(level, false) - { - } - - /// - /// Creates a new deflater with given compression level. - /// - /// - /// the compression level, a value between NO_COMPRESSION - /// and BEST_COMPRESSION. - /// - /// - /// true, if we should suppress the Zlib/RFC1950 header at the - /// beginning and the adler checksum at the end of the output. This is - /// useful for the GZIP/PKZIP formats. - /// - /// if lvl is out of range. - public Deflater(int level, bool noZlibHeaderOrFooter) - { - if (level == DEFAULT_COMPRESSION) - { - level = 6; - } - else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) - { - throw new ArgumentOutOfRangeException(nameof(level)); - } - - pending = new DeflaterPending(); - engine = new DeflaterEngine(pending, noZlibHeaderOrFooter); - this.noZlibHeaderOrFooter = noZlibHeaderOrFooter; - SetStrategy(DeflateStrategy.Default); - SetLevel(level); - Reset(); - } - - #endregion Constructors - - /// - /// Resets the deflater. The deflater acts afterwards as if it was - /// just created with the same compression level and strategy as it - /// had before. - /// - public void Reset() - { - state = (noZlibHeaderOrFooter ? BUSY_STATE : INIT_STATE); - totalOut = 0; - pending.Reset(); - engine.Reset(); - } - - /// - /// Gets the current adler checksum of the data that was processed so far. - /// - public int Adler - { - get - { - return engine.Adler; - } - } - - /// - /// Gets the number of input bytes processed so far. - /// - public long TotalIn - { - get - { - return engine.TotalIn; - } - } - - /// - /// Gets the number of output bytes so far. - /// - public long TotalOut - { - get - { - return totalOut; - } - } - - /// - /// Flushes the current input block. Further calls to deflate() will - /// produce enough output to inflate everything in the current input - /// block. This is not part of Sun's JDK so I have made it package - /// private. It is used by DeflaterOutputStream to implement - /// flush(). - /// - public void Flush() - { - state |= IS_FLUSHING; - } - - /// - /// Finishes the deflater with the current input block. It is an error - /// to give more input after this method was called. This method must - /// be called to force all bytes to be flushed. - /// - public void Finish() - { - state |= (IS_FLUSHING | IS_FINISHING); - } - - /// - /// Returns true if the stream was finished and no more output bytes - /// are available. - /// - public bool IsFinished - { - get - { - return (state == FINISHED_STATE) && pending.IsFlushed; - } - } - - /// - /// Returns true, if the input buffer is empty. - /// You should then call setInput(). - /// NOTE: This method can also return true when the stream - /// was finished. - /// - public bool IsNeedingInput - { - get - { - return engine.NeedsInput(); - } - } - - /// - /// Sets the data which should be compressed next. This should be only - /// called when needsInput indicates that more input is needed. - /// If you call setInput when needsInput() returns false, the - /// previous input that is still pending will be thrown away. - /// The given byte array should not be changed, before needsInput() returns - /// true again. - /// This call is equivalent to setInput(input, 0, input.length). - /// - /// - /// the buffer containing the input data. - /// - /// - /// if the buffer was finished() or ended(). - /// - public void SetInput(byte[] input) - { - SetInput(input, 0, input.Length); - } - - /// - /// Sets the data which should be compressed next. This should be - /// only called when needsInput indicates that more input is needed. - /// The given byte array should not be changed, before needsInput() returns - /// true again. - /// - /// - /// the buffer containing the input data. - /// - /// - /// the start of the data. - /// - /// - /// the number of data bytes of input. - /// - /// - /// if the buffer was Finish()ed or if previous input is still pending. - /// - public void SetInput(byte[] input, int offset, int count) - { - if ((state & IS_FINISHING) != 0) - { - throw new InvalidOperationException("Finish() already called"); - } - engine.SetInput(input, offset, count); - } - - /// - /// Sets the compression level. There is no guarantee of the exact - /// position of the change, but if you call this when needsInput is - /// true the change of compression level will occur somewhere near - /// before the end of the so far given input. - /// - /// - /// the new compression level. - /// - public void SetLevel(int level) - { - if (level == DEFAULT_COMPRESSION) - { - level = 6; - } - else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) - { - throw new ArgumentOutOfRangeException(nameof(level)); - } - - if (this.level != level) - { - this.level = level; - engine.SetLevel(level); - } - } - - /// - /// Get current compression level - /// - /// Returns the current compression level - public int GetLevel() - { - return level; - } - - /// - /// Sets the compression strategy. Strategy is one of - /// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact - /// position where the strategy is changed, the same as for - /// SetLevel() applies. - /// - /// - /// The new compression strategy. - /// - public void SetStrategy(DeflateStrategy strategy) - { - engine.Strategy = strategy; - } - - /// - /// Deflates the current input block with to the given array. - /// - /// - /// The buffer where compressed data is stored - /// - /// - /// The number of compressed bytes added to the output, or 0 if either - /// IsNeedingInput() or IsFinished returns true or length is zero. - /// - public int Deflate(byte[] output) - { - return Deflate(output, 0, output.Length); - } - - /// - /// Deflates the current input block to the given array. - /// - /// - /// Buffer to store the compressed data. - /// - /// - /// Offset into the output array. - /// - /// - /// The maximum number of bytes that may be stored. - /// - /// - /// The number of compressed bytes added to the output, or 0 if either - /// needsInput() or finished() returns true or length is zero. - /// - /// - /// If Finish() was previously called. - /// - /// - /// If offset or length don't match the array length. - /// - public int Deflate(byte[] output, int offset, int length) - { - int origLength = length; - - if (state == CLOSED_STATE) - { - throw new InvalidOperationException("Deflater closed"); - } - - if (state < BUSY_STATE) - { - // output header - int header = (DEFLATED + - ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8; - int level_flags = (level - 1) >> 1; - if (level_flags < 0 || level_flags > 3) - { - level_flags = 3; - } - header |= level_flags << 6; - if ((state & IS_SETDICT) != 0) - { - // Dictionary was set - header |= DeflaterConstants.PRESET_DICT; - } - header += 31 - (header % 31); - - pending.WriteShortMSB(header); - if ((state & IS_SETDICT) != 0) - { - int chksum = engine.Adler; - engine.ResetAdler(); - pending.WriteShortMSB(chksum >> 16); - pending.WriteShortMSB(chksum & 0xffff); - } - - state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING)); - } - - for (; ; ) - { - int count = pending.Flush(output, offset, length); - offset += count; - totalOut += count; - length -= count; - - if (length == 0 || state == FINISHED_STATE) - { - break; - } - - if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) - { - switch (state) - { - case BUSY_STATE: - // We need more input now - return origLength - length; - - case FLUSHING_STATE: - if (level != NO_COMPRESSION) - { - /* We have to supply some lookahead. 8 bit lookahead - * is needed by the zlib inflater, and we must fill - * the next byte, so that all bits are flushed. - */ - int neededbits = 8 + ((-pending.BitCount) & 7); - while (neededbits > 0) - { - /* write a static tree block consisting solely of - * an EOF: - */ - pending.WriteBits(2, 10); - neededbits -= 10; - } - } - state = BUSY_STATE; - break; - - case FINISHING_STATE: - pending.AlignToByte(); - - // Compressed data is complete. Write footer information if required. - if (!noZlibHeaderOrFooter) - { - int adler = engine.Adler; - pending.WriteShortMSB(adler >> 16); - pending.WriteShortMSB(adler & 0xffff); - } - state = FINISHED_STATE; - break; - } - } - } - return origLength - length; - } - - /// - /// Sets the dictionary which should be used in the deflate process. - /// This call is equivalent to setDictionary(dict, 0, dict.Length). - /// - /// - /// the dictionary. - /// - /// - /// if SetInput () or Deflate () were already called or another dictionary was already set. - /// - public void SetDictionary(byte[] dictionary) - { - SetDictionary(dictionary, 0, dictionary.Length); - } - - /// - /// Sets the dictionary which should be used in the deflate process. - /// The dictionary is a byte array containing strings that are - /// likely to occur in the data which should be compressed. The - /// dictionary is not stored in the compressed output, only a - /// checksum. To decompress the output you need to supply the same - /// dictionary again. - /// - /// - /// The dictionary data - /// - /// - /// The index where dictionary information commences. - /// - /// - /// The number of bytes in the dictionary. - /// - /// - /// If SetInput () or Deflate() were already called or another dictionary was already set. - /// - public void SetDictionary(byte[] dictionary, int index, int count) - { - if (state != INIT_STATE) - { - throw new InvalidOperationException(); - } - - state = SETDICT_STATE; - engine.SetDictionary(dictionary, index, count); - } - - #region Instance Fields - - /// - /// Compression level. - /// - private int level; - - /// - /// If true no Zlib/RFC1950 headers or footers are generated - /// - private bool noZlibHeaderOrFooter; - - /// - /// The current state. - /// - private int state; - - /// - /// The total bytes of output written. - /// - private long totalOut; - - /// - /// The pending output. - /// - private DeflaterPending pending; - - /// - /// The deflater engine. - /// - private DeflaterEngine engine; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterConstants.cs b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterConstants.cs deleted file mode 100644 index b7c7d2a6994b..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterConstants.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; - -namespace ICSharpCode.SharpZipLib.Zip.Compression -{ - /// - /// This class contains constants used for deflation. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "kept for backwards compatibility")] - public static class DeflaterConstants - { - /// - /// Set to true to enable debugging - /// - public const bool DEBUGGING = false; - - /// - /// Written to Zip file to identify a stored block - /// - public const int STORED_BLOCK = 0; - - /// - /// Identifies static tree in Zip file - /// - public const int STATIC_TREES = 1; - - /// - /// Identifies dynamic tree in Zip file - /// - public const int DYN_TREES = 2; - - /// - /// Header flag indicating a preset dictionary for deflation - /// - public const int PRESET_DICT = 0x20; - - /// - /// Sets internal buffer sizes for Huffman encoding - /// - public const int DEFAULT_MEM_LEVEL = 8; - - /// - /// Internal compression engine constant - /// - public const int MAX_MATCH = 258; - - /// - /// Internal compression engine constant - /// - public const int MIN_MATCH = 3; - - /// - /// Internal compression engine constant - /// - public const int MAX_WBITS = 15; - - /// - /// Internal compression engine constant - /// - public const int WSIZE = 1 << MAX_WBITS; - - /// - /// Internal compression engine constant - /// - public const int WMASK = WSIZE - 1; - - /// - /// Internal compression engine constant - /// - public const int HASH_BITS = DEFAULT_MEM_LEVEL + 7; - - /// - /// Internal compression engine constant - /// - public const int HASH_SIZE = 1 << HASH_BITS; - - /// - /// Internal compression engine constant - /// - public const int HASH_MASK = HASH_SIZE - 1; - - /// - /// Internal compression engine constant - /// - public const int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH; - - /// - /// Internal compression engine constant - /// - public const int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; - - /// - /// Internal compression engine constant - /// - public const int MAX_DIST = WSIZE - MIN_LOOKAHEAD; - - /// - /// Internal compression engine constant - /// - public const int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8); - - /// - /// Internal compression engine constant - /// - public static int MAX_BLOCK_SIZE = Math.Min(65535, PENDING_BUF_SIZE - 5); - - /// - /// Internal compression engine constant - /// - public const int DEFLATE_STORED = 0; - - /// - /// Internal compression engine constant - /// - public const int DEFLATE_FAST = 1; - - /// - /// Internal compression engine constant - /// - public const int DEFLATE_SLOW = 2; - - /// - /// Internal compression engine constant - /// - public static int[] GOOD_LENGTH = { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 }; - - /// - /// Internal compression engine constant - /// - public static int[] MAX_LAZY = { 0, 4, 5, 6, 4, 16, 16, 32, 128, 258 }; - - /// - /// Internal compression engine constant - /// - public static int[] NICE_LENGTH = { 0, 8, 16, 32, 16, 32, 128, 128, 258, 258 }; - - /// - /// Internal compression engine constant - /// - public static int[] MAX_CHAIN = { 0, 4, 8, 32, 16, 32, 128, 256, 1024, 4096 }; - - /// - /// Internal compression engine constant - /// - public static int[] COMPR_FUNC = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs deleted file mode 100644 index 556911c4033c..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs +++ /dev/null @@ -1,946 +0,0 @@ -using ICSharpCode.SharpZipLib.Checksum; -using System; - -namespace ICSharpCode.SharpZipLib.Zip.Compression -{ - /// - /// Strategies for deflater - /// - public enum DeflateStrategy - { - /// - /// The default strategy - /// - Default = 0, - - /// - /// This strategy will only allow longer string repetitions. It is - /// useful for random data with a small character set. - /// - Filtered = 1, - - /// - /// This strategy will not look for string repetitions at all. It - /// only encodes with Huffman trees (which means, that more common - /// characters get a smaller encoding. - /// - HuffmanOnly = 2 - } - - // DEFLATE ALGORITHM: - // - // The uncompressed stream is inserted into the window array. When - // the window array is full the first half is thrown away and the - // second half is copied to the beginning. - // - // The head array is a hash table. Three characters build a hash value - // and they the value points to the corresponding index in window of - // the last string with this hash. The prev array implements a - // linked list of matches with the same hash: prev[index & WMASK] points - // to the previous index with the same hash. - // - - /// - /// Low level compression engine for deflate algorithm which uses a 32K sliding window - /// with secondary compression from Huffman/Shannon-Fano codes. - /// - public class DeflaterEngine - { - #region Constants - - private const int TooFar = 4096; - - #endregion Constants - - #region Constructors - - /// - /// Construct instance with pending buffer - /// Adler calculation will be performed - /// - /// - /// Pending buffer to use - /// - public DeflaterEngine(DeflaterPending pending) - : this (pending, false) - { - } - - - - /// - /// Construct instance with pending buffer - /// - /// - /// Pending buffer to use - /// - /// - /// If no adler calculation should be performed - /// - public DeflaterEngine(DeflaterPending pending, bool noAdlerCalculation) - { - this.pending = pending; - huffman = new DeflaterHuffman(pending); - if (!noAdlerCalculation) - adler = new Adler32(); - - window = new byte[2 * DeflaterConstants.WSIZE]; - head = new short[DeflaterConstants.HASH_SIZE]; - prev = new short[DeflaterConstants.WSIZE]; - - // We start at index 1, to avoid an implementation deficiency, that - // we cannot build a repeat pattern at index 0. - blockStart = strstart = 1; - } - - #endregion Constructors - - /// - /// Deflate drives actual compression of data - /// - /// True to flush input buffers - /// Finish deflation with the current input. - /// Returns true if progress has been made. - public bool Deflate(bool flush, bool finish) - { - bool progress; - do - { - FillWindow(); - bool canFlush = flush && (inputOff == inputEnd); - -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) { - Console.WriteLine("window: [" + blockStart + "," + strstart + "," - + lookahead + "], " + compressionFunction + "," + canFlush); - } -#endif - switch (compressionFunction) - { - case DeflaterConstants.DEFLATE_STORED: - progress = DeflateStored(canFlush, finish); - break; - - case DeflaterConstants.DEFLATE_FAST: - progress = DeflateFast(canFlush, finish); - break; - - case DeflaterConstants.DEFLATE_SLOW: - progress = DeflateSlow(canFlush, finish); - break; - - default: - throw new InvalidOperationException("unknown compressionFunction"); - } - } while (pending.IsFlushed && progress); // repeat while we have no pending output and progress was made - return progress; - } - - /// - /// Sets input data to be deflated. Should only be called when NeedsInput() - /// returns true - /// - /// The buffer containing input data. - /// The offset of the first byte of data. - /// The number of bytes of data to use as input. - public void SetInput(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - if (inputOff < inputEnd) - { - throw new InvalidOperationException("Old input was not completely processed"); - } - - int end = offset + count; - - /* We want to throw an ArrayIndexOutOfBoundsException early. The - * check is very tricky: it also handles integer wrap around. - */ - if ((offset > end) || (end > buffer.Length)) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - inputBuf = buffer; - inputOff = offset; - inputEnd = end; - } - - /// - /// Determines if more input is needed. - /// - /// Return true if input is needed via SetInput - public bool NeedsInput() - { - return (inputEnd == inputOff); - } - - /// - /// Set compression dictionary - /// - /// The buffer containing the dictionary data - /// The offset in the buffer for the first byte of data - /// The length of the dictionary data. - public void SetDictionary(byte[] buffer, int offset, int length) - { -#if DebugDeflation - if (DeflaterConstants.DEBUGGING && (strstart != 1) ) - { - throw new InvalidOperationException("strstart not 1"); - } -#endif - adler?.Update(new ArraySegment(buffer, offset, length)); - if (length < DeflaterConstants.MIN_MATCH) - { - return; - } - - if (length > DeflaterConstants.MAX_DIST) - { - offset += length - DeflaterConstants.MAX_DIST; - length = DeflaterConstants.MAX_DIST; - } - - System.Array.Copy(buffer, offset, window, strstart, length); - - UpdateHash(); - --length; - while (--length > 0) - { - InsertString(); - strstart++; - } - strstart += 2; - blockStart = strstart; - } - - /// - /// Reset internal state - /// - public void Reset() - { - huffman.Reset(); - adler?.Reset(); - blockStart = strstart = 1; - lookahead = 0; - totalIn = 0; - prevAvailable = false; - matchLen = DeflaterConstants.MIN_MATCH - 1; - - for (int i = 0; i < DeflaterConstants.HASH_SIZE; i++) - { - head[i] = 0; - } - - for (int i = 0; i < DeflaterConstants.WSIZE; i++) - { - prev[i] = 0; - } - } - - /// - /// Reset Adler checksum - /// - public void ResetAdler() - { - adler?.Reset(); - } - - /// - /// Get current value of Adler checksum - /// - public int Adler - { - get - { - return (adler != null) ? unchecked((int)adler.Value) : 0; - } - } - - /// - /// Total data processed - /// - public long TotalIn - { - get - { - return totalIn; - } - } - - /// - /// Get/set the deflate strategy - /// - public DeflateStrategy Strategy - { - get - { - return strategy; - } - set - { - strategy = value; - } - } - - /// - /// Set the deflate level (0-9) - /// - /// The value to set the level to. - public void SetLevel(int level) - { - if ((level < 0) || (level > 9)) - { - throw new ArgumentOutOfRangeException(nameof(level)); - } - - goodLength = DeflaterConstants.GOOD_LENGTH[level]; - max_lazy = DeflaterConstants.MAX_LAZY[level]; - niceLength = DeflaterConstants.NICE_LENGTH[level]; - max_chain = DeflaterConstants.MAX_CHAIN[level]; - - if (DeflaterConstants.COMPR_FUNC[level] != compressionFunction) - { -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) { - Console.WriteLine("Change from " + compressionFunction + " to " - + DeflaterConstants.COMPR_FUNC[level]); - } -#endif - switch (compressionFunction) - { - case DeflaterConstants.DEFLATE_STORED: - if (strstart > blockStart) - { - huffman.FlushStoredBlock(window, blockStart, - strstart - blockStart, false); - blockStart = strstart; - } - UpdateHash(); - break; - - case DeflaterConstants.DEFLATE_FAST: - if (strstart > blockStart) - { - huffman.FlushBlock(window, blockStart, strstart - blockStart, - false); - blockStart = strstart; - } - break; - - case DeflaterConstants.DEFLATE_SLOW: - if (prevAvailable) - { - huffman.TallyLit(window[strstart - 1] & 0xff); - } - if (strstart > blockStart) - { - huffman.FlushBlock(window, blockStart, strstart - blockStart, false); - blockStart = strstart; - } - prevAvailable = false; - matchLen = DeflaterConstants.MIN_MATCH - 1; - break; - } - compressionFunction = DeflaterConstants.COMPR_FUNC[level]; - } - } - - /// - /// Fill the window - /// - public void FillWindow() - { - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - if (strstart >= DeflaterConstants.WSIZE + DeflaterConstants.MAX_DIST) - { - SlideWindow(); - } - - /* If there is not enough lookahead, but still some input left, - * read in the input - */ - if (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd) - { - int more = 2 * DeflaterConstants.WSIZE - lookahead - strstart; - - if (more > inputEnd - inputOff) - { - more = inputEnd - inputOff; - } - - System.Array.Copy(inputBuf, inputOff, window, strstart + lookahead, more); - adler?.Update(new ArraySegment(inputBuf, inputOff, more)); - - inputOff += more; - totalIn += more; - lookahead += more; - } - - if (lookahead >= DeflaterConstants.MIN_MATCH) - { - UpdateHash(); - } - } - - private void UpdateHash() - { - /* - if (DEBUGGING) { - Console.WriteLine("updateHash: "+strstart); - } - */ - ins_h = (window[strstart] << DeflaterConstants.HASH_SHIFT) ^ window[strstart + 1]; - } - - /// - /// Inserts the current string in the head hash and returns the previous - /// value for this hash. - /// - /// The previous hash value - private int InsertString() - { - short match; - int hash = ((ins_h << DeflaterConstants.HASH_SHIFT) ^ window[strstart + (DeflaterConstants.MIN_MATCH - 1)]) & DeflaterConstants.HASH_MASK; - -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) - { - if (hash != (((window[strstart] << (2*HASH_SHIFT)) ^ - (window[strstart + 1] << HASH_SHIFT) ^ - (window[strstart + 2])) & HASH_MASK)) { - throw new SharpZipBaseException("hash inconsistent: " + hash + "/" - +window[strstart] + "," - +window[strstart + 1] + "," - +window[strstart + 2] + "," + HASH_SHIFT); - } - } -#endif - prev[strstart & DeflaterConstants.WMASK] = match = head[hash]; - head[hash] = unchecked((short)strstart); - ins_h = hash; - return match & 0xffff; - } - - private void SlideWindow() - { - Array.Copy(window, DeflaterConstants.WSIZE, window, 0, DeflaterConstants.WSIZE); - matchStart -= DeflaterConstants.WSIZE; - strstart -= DeflaterConstants.WSIZE; - blockStart -= DeflaterConstants.WSIZE; - - // Slide the hash table (could be avoided with 32 bit values - // at the expense of memory usage). - for (int i = 0; i < DeflaterConstants.HASH_SIZE; ++i) - { - int m = head[i] & 0xffff; - head[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0); - } - - // Slide the prev table. - for (int i = 0; i < DeflaterConstants.WSIZE; i++) - { - int m = prev[i] & 0xffff; - prev[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0); - } - } - - /// - /// Find the best (longest) string in the window matching the - /// string starting at strstart. - /// - /// Preconditions: - /// - /// strstart + DeflaterConstants.MAX_MATCH <= window.length. - /// - /// - /// True if a match greater than the minimum length is found - private bool FindLongestMatch(int curMatch) - { - int match; - int scan = strstart; - // scanMax is the highest position that we can look at - int scanMax = scan + Math.Min(DeflaterConstants.MAX_MATCH, lookahead) - 1; - int limit = Math.Max(scan - DeflaterConstants.MAX_DIST, 0); - - byte[] window = this.window; - short[] prev = this.prev; - int chainLength = this.max_chain; - int niceLength = Math.Min(this.niceLength, lookahead); - - matchLen = Math.Max(matchLen, DeflaterConstants.MIN_MATCH - 1); - - if (scan + matchLen > scanMax) return false; - - byte scan_end1 = window[scan + matchLen - 1]; - byte scan_end = window[scan + matchLen]; - - // Do not waste too much time if we already have a good match: - if (matchLen >= this.goodLength) chainLength >>= 2; - - do - { - match = curMatch; - scan = strstart; - - if (window[match + matchLen] != scan_end - || window[match + matchLen - 1] != scan_end1 - || window[match] != window[scan] - || window[++match] != window[++scan]) - { - continue; - } - - // scan is set to strstart+1 and the comparison passed, so - // scanMax - scan is the maximum number of bytes we can compare. - // below we compare 8 bytes at a time, so first we compare - // (scanMax - scan) % 8 bytes, so the remainder is a multiple of 8 - - switch ((scanMax - scan) % 8) - { - case 1: - if (window[++scan] == window[++match]) break; - break; - - case 2: - if (window[++scan] == window[++match] - && window[++scan] == window[++match]) break; - break; - - case 3: - if (window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match]) break; - break; - - case 4: - if (window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match]) break; - break; - - case 5: - if (window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match]) break; - break; - - case 6: - if (window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match]) break; - break; - - case 7: - if (window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match]) break; - break; - } - - if (window[scan] == window[match]) - { - /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart + 258 unless lookahead is - * exhausted first. - */ - do - { - if (scan == scanMax) - { - ++scan; // advance to first position not matched - ++match; - - break; - } - } - while (window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match]); - } - - if (scan - strstart > matchLen) - { -#if DebugDeflation - if (DeflaterConstants.DEBUGGING && (ins_h == 0) ) - Console.Error.WriteLine("Found match: " + curMatch + "-" + (scan - strstart)); -#endif - - matchStart = curMatch; - matchLen = scan - strstart; - - if (matchLen >= niceLength) - break; - - scan_end1 = window[scan - 1]; - scan_end = window[scan]; - } - } while ((curMatch = (prev[curMatch & DeflaterConstants.WMASK] & 0xffff)) > limit && 0 != --chainLength); - - return matchLen >= DeflaterConstants.MIN_MATCH; - } - - private bool DeflateStored(bool flush, bool finish) - { - if (!flush && (lookahead == 0)) - { - return false; - } - - strstart += lookahead; - lookahead = 0; - - int storedLength = strstart - blockStart; - - if ((storedLength >= DeflaterConstants.MAX_BLOCK_SIZE) || // Block is full - (blockStart < DeflaterConstants.WSIZE && storedLength >= DeflaterConstants.MAX_DIST) || // Block may move out of window - flush) - { - bool lastBlock = finish; - if (storedLength > DeflaterConstants.MAX_BLOCK_SIZE) - { - storedLength = DeflaterConstants.MAX_BLOCK_SIZE; - lastBlock = false; - } - -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) - { - Console.WriteLine("storedBlock[" + storedLength + "," + lastBlock + "]"); - } -#endif - - huffman.FlushStoredBlock(window, blockStart, storedLength, lastBlock); - blockStart += storedLength; - return !(lastBlock || storedLength == 0); - } - return true; - } - - private bool DeflateFast(bool flush, bool finish) - { - if (lookahead < DeflaterConstants.MIN_LOOKAHEAD && !flush) - { - return false; - } - - while (lookahead >= DeflaterConstants.MIN_LOOKAHEAD || flush) - { - if (lookahead == 0) - { - // We are flushing everything - huffman.FlushBlock(window, blockStart, strstart - blockStart, finish); - blockStart = strstart; - return false; - } - - if (strstart > 2 * DeflaterConstants.WSIZE - DeflaterConstants.MIN_LOOKAHEAD) - { - /* slide window, as FindLongestMatch needs this. - * This should only happen when flushing and the window - * is almost full. - */ - SlideWindow(); - } - - int hashHead; - if (lookahead >= DeflaterConstants.MIN_MATCH && - (hashHead = InsertString()) != 0 && - strategy != DeflateStrategy.HuffmanOnly && - strstart - hashHead <= DeflaterConstants.MAX_DIST && - FindLongestMatch(hashHead)) - { - // longestMatch sets matchStart and matchLen -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) - { - for (int i = 0 ; i < matchLen; i++) { - if (window[strstart + i] != window[matchStart + i]) { - throw new SharpZipBaseException("Match failure"); - } - } - } -#endif - - bool full = huffman.TallyDist(strstart - matchStart, matchLen); - - lookahead -= matchLen; - if (matchLen <= max_lazy && lookahead >= DeflaterConstants.MIN_MATCH) - { - while (--matchLen > 0) - { - ++strstart; - InsertString(); - } - ++strstart; - } - else - { - strstart += matchLen; - if (lookahead >= DeflaterConstants.MIN_MATCH - 1) - { - UpdateHash(); - } - } - matchLen = DeflaterConstants.MIN_MATCH - 1; - if (!full) - { - continue; - } - } - else - { - // No match found - huffman.TallyLit(window[strstart] & 0xff); - ++strstart; - --lookahead; - } - - if (huffman.IsFull()) - { - bool lastBlock = finish && (lookahead == 0); - huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock); - blockStart = strstart; - return !lastBlock; - } - } - return true; - } - - private bool DeflateSlow(bool flush, bool finish) - { - if (lookahead < DeflaterConstants.MIN_LOOKAHEAD && !flush) - { - return false; - } - - while (lookahead >= DeflaterConstants.MIN_LOOKAHEAD || flush) - { - if (lookahead == 0) - { - if (prevAvailable) - { - huffman.TallyLit(window[strstart - 1] & 0xff); - } - prevAvailable = false; - - // We are flushing everything -#if DebugDeflation - if (DeflaterConstants.DEBUGGING && !flush) - { - throw new SharpZipBaseException("Not flushing, but no lookahead"); - } -#endif - huffman.FlushBlock(window, blockStart, strstart - blockStart, - finish); - blockStart = strstart; - return false; - } - - if (strstart >= 2 * DeflaterConstants.WSIZE - DeflaterConstants.MIN_LOOKAHEAD) - { - /* slide window, as FindLongestMatch needs this. - * This should only happen when flushing and the window - * is almost full. - */ - SlideWindow(); - } - - int prevMatch = matchStart; - int prevLen = matchLen; - if (lookahead >= DeflaterConstants.MIN_MATCH) - { - int hashHead = InsertString(); - - if (strategy != DeflateStrategy.HuffmanOnly && - hashHead != 0 && - strstart - hashHead <= DeflaterConstants.MAX_DIST && - FindLongestMatch(hashHead)) - { - // longestMatch sets matchStart and matchLen - - // Discard match if too small and too far away - if (matchLen <= 5 && (strategy == DeflateStrategy.Filtered || (matchLen == DeflaterConstants.MIN_MATCH && strstart - matchStart > TooFar))) - { - matchLen = DeflaterConstants.MIN_MATCH - 1; - } - } - } - - // previous match was better - if ((prevLen >= DeflaterConstants.MIN_MATCH) && (matchLen <= prevLen)) - { -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) - { - for (int i = 0 ; i < matchLen; i++) { - if (window[strstart-1+i] != window[prevMatch + i]) - throw new SharpZipBaseException(); - } - } -#endif - huffman.TallyDist(strstart - 1 - prevMatch, prevLen); - prevLen -= 2; - do - { - strstart++; - lookahead--; - if (lookahead >= DeflaterConstants.MIN_MATCH) - { - InsertString(); - } - } while (--prevLen > 0); - - strstart++; - lookahead--; - prevAvailable = false; - matchLen = DeflaterConstants.MIN_MATCH - 1; - } - else - { - if (prevAvailable) - { - huffman.TallyLit(window[strstart - 1] & 0xff); - } - prevAvailable = true; - strstart++; - lookahead--; - } - - if (huffman.IsFull()) - { - int len = strstart - blockStart; - if (prevAvailable) - { - len--; - } - bool lastBlock = (finish && (lookahead == 0) && !prevAvailable); - huffman.FlushBlock(window, blockStart, len, lastBlock); - blockStart += len; - return !lastBlock; - } - } - return true; - } - - #region Instance Fields - - // Hash index of string to be inserted - private int ins_h; - - /// - /// Hashtable, hashing three characters to an index for window, so - /// that window[index]..window[index+2] have this hash code. - /// Note that the array should really be unsigned short, so you need - /// to and the values with 0xffff. - /// - private short[] head; - - /// - /// prev[index & WMASK] points to the previous index that has the - /// same hash code as the string starting at index. This way - /// entries with the same hash code are in a linked list. - /// Note that the array should really be unsigned short, so you need - /// to and the values with 0xffff. - /// - private short[] prev; - - private int matchStart; - - // Length of best match - private int matchLen; - - // Set if previous match exists - private bool prevAvailable; - - private int blockStart; - - /// - /// Points to the current character in the window. - /// - private int strstart; - - /// - /// lookahead is the number of characters starting at strstart in - /// window that are valid. - /// So window[strstart] until window[strstart+lookahead-1] are valid - /// characters. - /// - private int lookahead; - - /// - /// This array contains the part of the uncompressed stream that - /// is of relevance. The current character is indexed by strstart. - /// - private byte[] window; - - private DeflateStrategy strategy; - private int max_chain, max_lazy, niceLength, goodLength; - - /// - /// The current compression function. - /// - private int compressionFunction; - - /// - /// The input data for compression. - /// - private byte[] inputBuf; - - /// - /// The total bytes of input read. - /// - private long totalIn; - - /// - /// The offset into inputBuf, where input data starts. - /// - private int inputOff; - - /// - /// The end offset of the input data. - /// - private int inputEnd; - - private DeflaterPending pending; - private DeflaterHuffman huffman; - - /// - /// The adler checksum - /// - private Adler32 adler; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterHuffman.cs b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterHuffman.cs deleted file mode 100644 index 2f71366fca72..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterHuffman.cs +++ /dev/null @@ -1,959 +0,0 @@ -using System; - -namespace ICSharpCode.SharpZipLib.Zip.Compression -{ - /// - /// This is the DeflaterHuffman class. - /// - /// This class is not thread safe. This is inherent in the API, due - /// to the split of Deflate and SetInput. - /// - /// author of the original java version : Jochen Hoenicke - /// - public class DeflaterHuffman - { - private const int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6); - private const int LITERAL_NUM = 286; - - // Number of distance codes - private const int DIST_NUM = 30; - - // Number of codes used to transfer bit lengths - private const int BITLEN_NUM = 19; - - // repeat previous bit length 3-6 times (2 bits of repeat count) - private const int REP_3_6 = 16; - - // repeat a zero length 3-10 times (3 bits of repeat count) - private const int REP_3_10 = 17; - - // repeat a zero length 11-138 times (7 bits of repeat count) - private const int REP_11_138 = 18; - - private const int EOF_SYMBOL = 256; - - // The lengths of the bit length codes are sent in order of decreasing - // probability, to avoid transmitting the lengths for unused bit length codes. - private static readonly int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - - private static readonly byte[] bit4Reverse = { - 0, - 8, - 4, - 12, - 2, - 10, - 6, - 14, - 1, - 9, - 5, - 13, - 3, - 11, - 7, - 15 - }; - - private static short[] staticLCodes; - private static byte[] staticLLength; - private static short[] staticDCodes; - private static byte[] staticDLength; - - private class Tree - { - #region Instance Fields - - public short[] freqs; - - public byte[] length; - - public int minNumCodes; - - public int numCodes; - - private short[] codes; - private readonly int[] bl_counts; - private readonly int maxLength; - private DeflaterHuffman dh; - - #endregion Instance Fields - - #region Constructors - - public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength) - { - this.dh = dh; - this.minNumCodes = minCodes; - this.maxLength = maxLength; - freqs = new short[elems]; - bl_counts = new int[maxLength]; - } - - #endregion Constructors - - /// - /// Resets the internal state of the tree - /// - public void Reset() - { - for (int i = 0; i < freqs.Length; i++) - { - freqs[i] = 0; - } - codes = null; - length = null; - } - - public void WriteSymbol(int code) - { - // if (DeflaterConstants.DEBUGGING) { - // freqs[code]--; - // // Console.Write("writeSymbol("+freqs.length+","+code+"): "); - // } - dh.pending.WriteBits(codes[code] & 0xffff, length[code]); - } - - /// - /// Check that all frequencies are zero - /// - /// - /// At least one frequency is non-zero - /// - public void CheckEmpty() - { - bool empty = true; - for (int i = 0; i < freqs.Length; i++) - { - empty &= freqs[i] == 0; - } - - if (!empty) - { - throw new SharpZipBaseException("!Empty"); - } - } - - /// - /// Set static codes and length - /// - /// new codes - /// length for new codes - public void SetStaticCodes(short[] staticCodes, byte[] staticLengths) - { - codes = staticCodes; - length = staticLengths; - } - - /// - /// Build dynamic codes and lengths - /// - public void BuildCodes() - { - int numSymbols = freqs.Length; - int[] nextCode = new int[maxLength]; - int code = 0; - - codes = new short[freqs.Length]; - - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("buildCodes: "+freqs.Length); - // } - - for (int bits = 0; bits < maxLength; bits++) - { - nextCode[bits] = code; - code += bl_counts[bits] << (15 - bits); - - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("bits: " + ( bits + 1) + " count: " + bl_counts[bits] - // +" nextCode: "+code); - // } - } - -#if DebugDeflation - if ( DeflaterConstants.DEBUGGING && (code != 65536) ) - { - throw new SharpZipBaseException("Inconsistent bl_counts!"); - } -#endif - for (int i = 0; i < numCodes; i++) - { - int bits = length[i]; - if (bits > 0) - { - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("codes["+i+"] = rev(" + nextCode[bits-1]+"), - // +bits); - // } - - codes[i] = BitReverse(nextCode[bits - 1]); - nextCode[bits - 1] += 1 << (16 - bits); - } - } - } - - public void BuildTree() - { - int numSymbols = freqs.Length; - - /* heap is a priority queue, sorted by frequency, least frequent - * nodes first. The heap is a binary tree, with the property, that - * the parent node is smaller than both child nodes. This assures - * that the smallest node is the first parent. - * - * The binary tree is encoded in an array: 0 is root node and - * the nodes 2*n+1, 2*n+2 are the child nodes of node n. - */ - int[] heap = new int[numSymbols]; - int heapLen = 0; - int maxCode = 0; - for (int n = 0; n < numSymbols; n++) - { - int freq = freqs[n]; - if (freq != 0) - { - // Insert n into heap - int pos = heapLen++; - int ppos; - while (pos > 0 && freqs[heap[ppos = (pos - 1) / 2]] > freq) - { - heap[pos] = heap[ppos]; - pos = ppos; - } - heap[pos] = n; - - maxCode = n; - } - } - - /* We could encode a single literal with 0 bits but then we - * don't see the literals. Therefore we force at least two - * literals to avoid this case. We don't care about order in - * this case, both literals get a 1 bit code. - */ - while (heapLen < 2) - { - int node = maxCode < 2 ? ++maxCode : 0; - heap[heapLen++] = node; - } - - numCodes = Math.Max(maxCode + 1, minNumCodes); - - int numLeafs = heapLen; - int[] childs = new int[4 * heapLen - 2]; - int[] values = new int[2 * heapLen - 1]; - int numNodes = numLeafs; - for (int i = 0; i < heapLen; i++) - { - int node = heap[i]; - childs[2 * i] = node; - childs[2 * i + 1] = -1; - values[i] = freqs[node] << 8; - heap[i] = i; - } - - /* Construct the Huffman tree by repeatedly combining the least two - * frequent nodes. - */ - do - { - int first = heap[0]; - int last = heap[--heapLen]; - - // Propagate the hole to the leafs of the heap - int ppos = 0; - int path = 1; - - while (path < heapLen) - { - if (path + 1 < heapLen && values[heap[path]] > values[heap[path + 1]]) - { - path++; - } - - heap[ppos] = heap[path]; - ppos = path; - path = path * 2 + 1; - } - - /* Now propagate the last element down along path. Normally - * it shouldn't go too deep. - */ - int lastVal = values[last]; - while ((path = ppos) > 0 && values[heap[ppos = (path - 1) / 2]] > lastVal) - { - heap[path] = heap[ppos]; - } - heap[path] = last; - - int second = heap[0]; - - // Create a new node father of first and second - last = numNodes++; - childs[2 * last] = first; - childs[2 * last + 1] = second; - int mindepth = Math.Min(values[first] & 0xff, values[second] & 0xff); - values[last] = lastVal = values[first] + values[second] - mindepth + 1; - - // Again, propagate the hole to the leafs - ppos = 0; - path = 1; - - while (path < heapLen) - { - if (path + 1 < heapLen && values[heap[path]] > values[heap[path + 1]]) - { - path++; - } - - heap[ppos] = heap[path]; - ppos = path; - path = ppos * 2 + 1; - } - - // Now propagate the new element down along path - while ((path = ppos) > 0 && values[heap[ppos = (path - 1) / 2]] > lastVal) - { - heap[path] = heap[ppos]; - } - heap[path] = last; - } while (heapLen > 1); - - if (heap[0] != childs.Length / 2 - 1) - { - throw new SharpZipBaseException("Heap invariant violated"); - } - - BuildLength(childs); - } - - /// - /// Get encoded length - /// - /// Encoded length, the sum of frequencies * lengths - public int GetEncodedLength() - { - int len = 0; - for (int i = 0; i < freqs.Length; i++) - { - len += freqs[i] * length[i]; - } - return len; - } - - /// - /// Scan a literal or distance tree to determine the frequencies of the codes - /// in the bit length tree. - /// - public void CalcBLFreq(Tree blTree) - { - int max_count; /* max repeat count */ - int min_count; /* min repeat count */ - int count; /* repeat count of the current code */ - int curlen = -1; /* length of current code */ - - int i = 0; - while (i < numCodes) - { - count = 1; - int nextlen = length[i]; - if (nextlen == 0) - { - max_count = 138; - min_count = 3; - } - else - { - max_count = 6; - min_count = 3; - if (curlen != nextlen) - { - blTree.freqs[nextlen]++; - count = 0; - } - } - curlen = nextlen; - i++; - - while (i < numCodes && curlen == length[i]) - { - i++; - if (++count >= max_count) - { - break; - } - } - - if (count < min_count) - { - blTree.freqs[curlen] += (short)count; - } - else if (curlen != 0) - { - blTree.freqs[REP_3_6]++; - } - else if (count <= 10) - { - blTree.freqs[REP_3_10]++; - } - else - { - blTree.freqs[REP_11_138]++; - } - } - } - - /// - /// Write tree values - /// - /// Tree to write - public void WriteTree(Tree blTree) - { - int max_count; // max repeat count - int min_count; // min repeat count - int count; // repeat count of the current code - int curlen = -1; // length of current code - - int i = 0; - while (i < numCodes) - { - count = 1; - int nextlen = length[i]; - if (nextlen == 0) - { - max_count = 138; - min_count = 3; - } - else - { - max_count = 6; - min_count = 3; - if (curlen != nextlen) - { - blTree.WriteSymbol(nextlen); - count = 0; - } - } - curlen = nextlen; - i++; - - while (i < numCodes && curlen == length[i]) - { - i++; - if (++count >= max_count) - { - break; - } - } - - if (count < min_count) - { - while (count-- > 0) - { - blTree.WriteSymbol(curlen); - } - } - else if (curlen != 0) - { - blTree.WriteSymbol(REP_3_6); - dh.pending.WriteBits(count - 3, 2); - } - else if (count <= 10) - { - blTree.WriteSymbol(REP_3_10); - dh.pending.WriteBits(count - 3, 3); - } - else - { - blTree.WriteSymbol(REP_11_138); - dh.pending.WriteBits(count - 11, 7); - } - } - } - - private void BuildLength(int[] childs) - { - this.length = new byte[freqs.Length]; - int numNodes = childs.Length / 2; - int numLeafs = (numNodes + 1) / 2; - int overflow = 0; - - for (int i = 0; i < maxLength; i++) - { - bl_counts[i] = 0; - } - - // First calculate optimal bit lengths - int[] lengths = new int[numNodes]; - lengths[numNodes - 1] = 0; - - for (int i = numNodes - 1; i >= 0; i--) - { - if (childs[2 * i + 1] != -1) - { - int bitLength = lengths[i] + 1; - if (bitLength > maxLength) - { - bitLength = maxLength; - overflow++; - } - lengths[childs[2 * i]] = lengths[childs[2 * i + 1]] = bitLength; - } - else - { - // A leaf node - int bitLength = lengths[i]; - bl_counts[bitLength - 1]++; - this.length[childs[2 * i]] = (byte)lengths[i]; - } - } - - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("Tree "+freqs.Length+" lengths:"); - // for (int i=0; i < numLeafs; i++) { - // //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] - // + " len: "+length[childs[2*i]]); - // } - // } - - if (overflow == 0) - { - return; - } - - int incrBitLen = maxLength - 1; - do - { - // Find the first bit length which could increase: - while (bl_counts[--incrBitLen] == 0) - { - } - - // Move this node one down and remove a corresponding - // number of overflow nodes. - do - { - bl_counts[incrBitLen]--; - bl_counts[++incrBitLen]++; - overflow -= 1 << (maxLength - 1 - incrBitLen); - } while (overflow > 0 && incrBitLen < maxLength - 1); - } while (overflow > 0); - - /* We may have overshot above. Move some nodes from maxLength to - * maxLength-1 in that case. - */ - bl_counts[maxLength - 1] += overflow; - bl_counts[maxLength - 2] -= overflow; - - /* Now recompute all bit lengths, scanning in increasing - * frequency. It is simpler to reconstruct all lengths instead of - * fixing only the wrong ones. This idea is taken from 'ar' - * written by Haruhiko Okumura. - * - * The nodes were inserted with decreasing frequency into the childs - * array. - */ - int nodePtr = 2 * numLeafs; - for (int bits = maxLength; bits != 0; bits--) - { - int n = bl_counts[bits - 1]; - while (n > 0) - { - int childPtr = 2 * childs[nodePtr++]; - if (childs[childPtr + 1] == -1) - { - // We found another leaf - length[childs[childPtr]] = (byte)bits; - n--; - } - } - } - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("*** After overflow elimination. ***"); - // for (int i=0; i < numLeafs; i++) { - // //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] - // + " len: "+length[childs[2*i]]); - // } - // } - } - } - - #region Instance Fields - - /// - /// Pending buffer to use - /// - public DeflaterPending pending; - - private Tree literalTree; - private Tree distTree; - private Tree blTree; - - // Buffer for distances - private short[] d_buf; - - private byte[] l_buf; - private int last_lit; - private int extra_bits; - - #endregion Instance Fields - - static DeflaterHuffman() - { - // See RFC 1951 3.2.6 - // Literal codes - staticLCodes = new short[LITERAL_NUM]; - staticLLength = new byte[LITERAL_NUM]; - - int i = 0; - while (i < 144) - { - staticLCodes[i] = BitReverse((0x030 + i) << 8); - staticLLength[i++] = 8; - } - - while (i < 256) - { - staticLCodes[i] = BitReverse((0x190 - 144 + i) << 7); - staticLLength[i++] = 9; - } - - while (i < 280) - { - staticLCodes[i] = BitReverse((0x000 - 256 + i) << 9); - staticLLength[i++] = 7; - } - - while (i < LITERAL_NUM) - { - staticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8); - staticLLength[i++] = 8; - } - - // Distance codes - staticDCodes = new short[DIST_NUM]; - staticDLength = new byte[DIST_NUM]; - for (i = 0; i < DIST_NUM; i++) - { - staticDCodes[i] = BitReverse(i << 11); - staticDLength[i] = 5; - } - } - - /// - /// Construct instance with pending buffer - /// - /// Pending buffer to use - public DeflaterHuffman(DeflaterPending pending) - { - this.pending = pending; - - literalTree = new Tree(this, LITERAL_NUM, 257, 15); - distTree = new Tree(this, DIST_NUM, 1, 15); - blTree = new Tree(this, BITLEN_NUM, 4, 7); - - d_buf = new short[BUFSIZE]; - l_buf = new byte[BUFSIZE]; - } - - /// - /// Reset internal state - /// - public void Reset() - { - last_lit = 0; - extra_bits = 0; - literalTree.Reset(); - distTree.Reset(); - blTree.Reset(); - } - - /// - /// Write all trees to pending buffer - /// - /// The number/rank of treecodes to send. - public void SendAllTrees(int blTreeCodes) - { - blTree.BuildCodes(); - literalTree.BuildCodes(); - distTree.BuildCodes(); - pending.WriteBits(literalTree.numCodes - 257, 5); - pending.WriteBits(distTree.numCodes - 1, 5); - pending.WriteBits(blTreeCodes - 4, 4); - for (int rank = 0; rank < blTreeCodes; rank++) - { - pending.WriteBits(blTree.length[BL_ORDER[rank]], 3); - } - literalTree.WriteTree(blTree); - distTree.WriteTree(blTree); - -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) { - blTree.CheckEmpty(); - } -#endif - } - - /// - /// Compress current buffer writing data to pending buffer - /// - public void CompressBlock() - { - for (int i = 0; i < last_lit; i++) - { - int litlen = l_buf[i] & 0xff; - int dist = d_buf[i]; - if (dist-- != 0) - { - // if (DeflaterConstants.DEBUGGING) { - // Console.Write("["+(dist+1)+","+(litlen+3)+"]: "); - // } - - int lc = Lcode(litlen); - literalTree.WriteSymbol(lc); - - int bits = (lc - 261) / 4; - if (bits > 0 && bits <= 5) - { - pending.WriteBits(litlen & ((1 << bits) - 1), bits); - } - - int dc = Dcode(dist); - distTree.WriteSymbol(dc); - - bits = dc / 2 - 1; - if (bits > 0) - { - pending.WriteBits(dist & ((1 << bits) - 1), bits); - } - } - else - { - // if (DeflaterConstants.DEBUGGING) { - // if (litlen > 32 && litlen < 127) { - // Console.Write("("+(char)litlen+"): "); - // } else { - // Console.Write("{"+litlen+"}: "); - // } - // } - literalTree.WriteSymbol(litlen); - } - } - -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) { - Console.Write("EOF: "); - } -#endif - literalTree.WriteSymbol(EOF_SYMBOL); - -#if DebugDeflation - if (DeflaterConstants.DEBUGGING) { - literalTree.CheckEmpty(); - distTree.CheckEmpty(); - } -#endif - } - - /// - /// Flush block to output with no compression - /// - /// Data to write - /// Index of first byte to write - /// Count of bytes to write - /// True if this is the last block - public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) - { -#if DebugDeflation - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("Flushing stored block "+ storedLength); - // } -#endif - pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0), 3); - pending.AlignToByte(); - pending.WriteShort(storedLength); - pending.WriteShort(~storedLength); - pending.WriteBlock(stored, storedOffset, storedLength); - Reset(); - } - - /// - /// Flush block to output with compression - /// - /// Data to flush - /// Index of first byte to flush - /// Count of bytes to flush - /// True if this is the last block - public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) - { - literalTree.freqs[EOF_SYMBOL]++; - - // Build trees - literalTree.BuildTree(); - distTree.BuildTree(); - - // Calculate bitlen frequency - literalTree.CalcBLFreq(blTree); - distTree.CalcBLFreq(blTree); - - // Build bitlen tree - blTree.BuildTree(); - - int blTreeCodes = 4; - for (int i = 18; i > blTreeCodes; i--) - { - if (blTree.length[BL_ORDER[i]] > 0) - { - blTreeCodes = i + 1; - } - } - int opt_len = 14 + blTreeCodes * 3 + blTree.GetEncodedLength() + - literalTree.GetEncodedLength() + distTree.GetEncodedLength() + - extra_bits; - - int static_len = extra_bits; - for (int i = 0; i < LITERAL_NUM; i++) - { - static_len += literalTree.freqs[i] * staticLLength[i]; - } - for (int i = 0; i < DIST_NUM; i++) - { - static_len += distTree.freqs[i] * staticDLength[i]; - } - if (opt_len >= static_len) - { - // Force static trees - opt_len = static_len; - } - - if (storedOffset >= 0 && storedLength + 4 < opt_len >> 3) - { - // Store Block - - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("Storing, since " + storedLength + " < " + opt_len - // + " <= " + static_len); - // } - FlushStoredBlock(stored, storedOffset, storedLength, lastBlock); - } - else if (opt_len == static_len) - { - // Encode with static tree - pending.WriteBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0), 3); - literalTree.SetStaticCodes(staticLCodes, staticLLength); - distTree.SetStaticCodes(staticDCodes, staticDLength); - CompressBlock(); - Reset(); - } - else - { - // Encode with dynamic tree - pending.WriteBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0), 3); - SendAllTrees(blTreeCodes); - CompressBlock(); - Reset(); - } - } - - /// - /// Get value indicating if internal buffer is full - /// - /// true if buffer is full - public bool IsFull() - { - return last_lit >= BUFSIZE; - } - - /// - /// Add literal to buffer - /// - /// Literal value to add to buffer. - /// Value indicating internal buffer is full - public bool TallyLit(int literal) - { - // if (DeflaterConstants.DEBUGGING) { - // if (lit > 32 && lit < 127) { - // //Console.WriteLine("("+(char)lit+")"); - // } else { - // //Console.WriteLine("{"+lit+"}"); - // } - // } - d_buf[last_lit] = 0; - l_buf[last_lit++] = (byte)literal; - literalTree.freqs[literal]++; - return IsFull(); - } - - /// - /// Add distance code and length to literal and distance trees - /// - /// Distance code - /// Length - /// Value indicating if internal buffer is full - public bool TallyDist(int distance, int length) - { - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("[" + distance + "," + length + "]"); - // } - - d_buf[last_lit] = (short)distance; - l_buf[last_lit++] = (byte)(length - 3); - - int lc = Lcode(length - 3); - literalTree.freqs[lc]++; - if (lc >= 265 && lc < 285) - { - extra_bits += (lc - 261) / 4; - } - - int dc = Dcode(distance - 1); - distTree.freqs[dc]++; - if (dc >= 4) - { - extra_bits += dc / 2 - 1; - } - return IsFull(); - } - - /// - /// Reverse the bits of a 16 bit value. - /// - /// Value to reverse bits - /// Value with bits reversed - public static short BitReverse(int toReverse) - { - return (short)(bit4Reverse[toReverse & 0xF] << 12 | - bit4Reverse[(toReverse >> 4) & 0xF] << 8 | - bit4Reverse[(toReverse >> 8) & 0xF] << 4 | - bit4Reverse[toReverse >> 12]); - } - - private static int Lcode(int length) - { - if (length == 255) - { - return 285; - } - - int code = 257; - while (length >= 8) - { - code += 4; - length >>= 1; - } - return code + length; - } - - private static int Dcode(int distance) - { - int code = 0; - while (distance >= 4) - { - code += 2; - distance >>= 1; - } - return code + distance; - } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterPending.cs b/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterPending.cs deleted file mode 100644 index 80d3e2142e34..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterPending.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace ICSharpCode.SharpZipLib.Zip.Compression -{ - /// - /// This class stores the pending output of the Deflater. - /// - /// author of the original java version : Jochen Hoenicke - /// - public class DeflaterPending : PendingBuffer - { - /// - /// Construct instance with default buffer size - /// - public DeflaterPending() : base(DeflaterConstants.PENDING_BUF_SIZE) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Inflater.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Inflater.cs deleted file mode 100644 index 439b4c6010eb..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/Inflater.cs +++ /dev/null @@ -1,887 +0,0 @@ -using ICSharpCode.SharpZipLib.Checksum; -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; -using System; - -namespace ICSharpCode.SharpZipLib.Zip.Compression -{ - /// - /// Inflater is used to decompress data that has been compressed according - /// to the "deflate" standard described in rfc1951. - /// - /// By default Zlib (rfc1950) headers and footers are expected in the input. - /// You can use constructor public Inflater(bool noHeader) passing true - /// if there is no Zlib header information - /// - /// The usage is as following. First you have to set some input with - /// SetInput(), then Inflate() it. If inflate doesn't - /// inflate any bytes there may be three reasons: - ///
    - ///
  • IsNeedingInput() returns true because the input buffer is empty. - /// You have to provide more input with SetInput(). - /// NOTE: IsNeedingInput() also returns true when, the stream is finished. - ///
  • - ///
  • IsNeedingDictionary() returns true, you have to provide a preset - /// dictionary with SetDictionary().
  • - ///
  • IsFinished returns true, the inflater has finished.
  • - ///
- /// Once the first output byte is produced, a dictionary will not be - /// needed at a later stage. - /// - /// author of the original java version : John Leuner, Jochen Hoenicke - ///
- public class Inflater - { - #region Constants/Readonly - - /// - /// Copy lengths for literal codes 257..285 - /// - private static readonly int[] CPLENS = { - 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, 258 - }; - - /// - /// Extra bits for literal codes 257..285 - /// - private static readonly int[] CPLEXT = { - 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 - }; - - /// - /// Copy offsets for distance codes 0..29 - /// - private static readonly int[] CPDIST = { - 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 - }; - - /// - /// Extra bits for distance codes - /// - private static readonly int[] CPDEXT = { - 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 - }; - - /// - /// These are the possible states for an inflater - /// - private const int DECODE_HEADER = 0; - - private const int DECODE_DICT = 1; - private const int DECODE_BLOCKS = 2; - private const int DECODE_STORED_LEN1 = 3; - private const int DECODE_STORED_LEN2 = 4; - private const int DECODE_STORED = 5; - private const int DECODE_DYN_HEADER = 6; - private const int DECODE_HUFFMAN = 7; - private const int DECODE_HUFFMAN_LENBITS = 8; - private const int DECODE_HUFFMAN_DIST = 9; - private const int DECODE_HUFFMAN_DISTBITS = 10; - private const int DECODE_CHKSUM = 11; - private const int FINISHED = 12; - - #endregion Constants/Readonly - - #region Instance Fields - - /// - /// This variable contains the current state. - /// - private int mode; - - /// - /// The adler checksum of the dictionary or of the decompressed - /// stream, as it is written in the header resp. footer of the - /// compressed stream. - /// Only valid if mode is DECODE_DICT or DECODE_CHKSUM. - /// - private int readAdler; - - /// - /// The number of bits needed to complete the current state. This - /// is valid, if mode is DECODE_DICT, DECODE_CHKSUM, - /// DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS. - /// - private int neededBits; - - private int repLength; - private int repDist; - private int uncomprLen; - - /// - /// True, if the last block flag was set in the last block of the - /// inflated stream. This means that the stream ends after the - /// current block. - /// - private bool isLastBlock; - - /// - /// The total number of inflated bytes. - /// - private long totalOut; - - /// - /// The total number of bytes set with setInput(). This is not the - /// value returned by the TotalIn property, since this also includes the - /// unprocessed input. - /// - private long totalIn; - - /// - /// This variable stores the noHeader flag that was given to the constructor. - /// True means, that the inflated stream doesn't contain a Zlib header or - /// footer. - /// - private bool noHeader; - - private readonly StreamManipulator input; - private OutputWindow outputWindow; - private InflaterDynHeader dynHeader; - private InflaterHuffmanTree litlenTree, distTree; - private Adler32 adler; - - #endregion Instance Fields - - #region Constructors - - /// - /// Creates a new inflater or RFC1951 decompressor - /// RFC1950/Zlib headers and footers will be expected in the input data - /// - public Inflater() : this(false) - { - } - - /// - /// Creates a new inflater. - /// - /// - /// True if no RFC1950/Zlib header and footer fields are expected in the input data - /// - /// This is used for GZIPed/Zipped input. - /// - /// For compatibility with - /// Sun JDK you should provide one byte of input more than needed in - /// this case. - /// - public Inflater(bool noHeader) - { - this.noHeader = noHeader; - if (!noHeader) - this.adler = new Adler32(); - input = new StreamManipulator(); - outputWindow = new OutputWindow(); - mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER; - } - - #endregion Constructors - - /// - /// Resets the inflater so that a new stream can be decompressed. All - /// pending input and output will be discarded. - /// - public void Reset() - { - mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER; - totalIn = 0; - totalOut = 0; - input.Reset(); - outputWindow.Reset(); - dynHeader = null; - litlenTree = null; - distTree = null; - isLastBlock = false; - adler?.Reset(); - } - - /// - /// Decodes a zlib/RFC1950 header. - /// - /// - /// False if more input is needed. - /// - /// - /// The header is invalid. - /// - private bool DecodeHeader() - { - int header = input.PeekBits(16); - if (header < 0) - { - return false; - } - input.DropBits(16); - - // The header is written in "wrong" byte order - header = ((header << 8) | (header >> 8)) & 0xffff; - if (header % 31 != 0) - { - throw new SharpZipBaseException("Header checksum illegal"); - } - - if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) - { - throw new SharpZipBaseException("Compression Method unknown"); - } - - /* Maximum size of the backwards window in bits. - * We currently ignore this, but we could use it to make the - * inflater window more space efficient. On the other hand the - * full window (15 bits) is needed most times, anyway. - int max_wbits = ((header & 0x7000) >> 12) + 8; - */ - - if ((header & 0x0020) == 0) - { // Dictionary flag? - mode = DECODE_BLOCKS; - } - else - { - mode = DECODE_DICT; - neededBits = 32; - } - return true; - } - - /// - /// Decodes the dictionary checksum after the deflate header. - /// - /// - /// False if more input is needed. - /// - private bool DecodeDict() - { - while (neededBits > 0) - { - int dictByte = input.PeekBits(8); - if (dictByte < 0) - { - return false; - } - input.DropBits(8); - readAdler = (readAdler << 8) | dictByte; - neededBits -= 8; - } - return false; - } - - /// - /// Decodes the huffman encoded symbols in the input stream. - /// - /// - /// false if more input is needed, true if output window is - /// full or the current block ends. - /// - /// - /// if deflated stream is invalid. - /// - private bool DecodeHuffman() - { - int free = outputWindow.GetFreeSpace(); - while (free >= 258) - { - int symbol; - switch (mode) - { - case DECODE_HUFFMAN: - // This is the inner loop so it is optimized a bit - while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0) - { - outputWindow.Write(symbol); - if (--free < 258) - { - return true; - } - } - - if (symbol < 257) - { - if (symbol < 0) - { - return false; - } - else - { - // symbol == 256: end of block - distTree = null; - litlenTree = null; - mode = DECODE_BLOCKS; - return true; - } - } - - try - { - repLength = CPLENS[symbol - 257]; - neededBits = CPLEXT[symbol - 257]; - } - catch (Exception) - { - throw new SharpZipBaseException("Illegal rep length code"); - } - goto case DECODE_HUFFMAN_LENBITS; // fall through - - case DECODE_HUFFMAN_LENBITS: - if (neededBits > 0) - { - mode = DECODE_HUFFMAN_LENBITS; - int i = input.PeekBits(neededBits); - if (i < 0) - { - return false; - } - input.DropBits(neededBits); - repLength += i; - } - mode = DECODE_HUFFMAN_DIST; - goto case DECODE_HUFFMAN_DIST; // fall through - - case DECODE_HUFFMAN_DIST: - symbol = distTree.GetSymbol(input); - if (symbol < 0) - { - return false; - } - - try - { - repDist = CPDIST[symbol]; - neededBits = CPDEXT[symbol]; - } - catch (Exception) - { - throw new SharpZipBaseException("Illegal rep dist code"); - } - - goto case DECODE_HUFFMAN_DISTBITS; // fall through - - case DECODE_HUFFMAN_DISTBITS: - if (neededBits > 0) - { - mode = DECODE_HUFFMAN_DISTBITS; - int i = input.PeekBits(neededBits); - if (i < 0) - { - return false; - } - input.DropBits(neededBits); - repDist += i; - } - - outputWindow.Repeat(repLength, repDist); - free -= repLength; - mode = DECODE_HUFFMAN; - break; - - default: - throw new SharpZipBaseException("Inflater unknown mode"); - } - } - return true; - } - - /// - /// Decodes the adler checksum after the deflate stream. - /// - /// - /// false if more input is needed. - /// - /// - /// If checksum doesn't match. - /// - private bool DecodeChksum() - { - while (neededBits > 0) - { - int chkByte = input.PeekBits(8); - if (chkByte < 0) - { - return false; - } - input.DropBits(8); - readAdler = (readAdler << 8) | chkByte; - neededBits -= 8; - } - - if ((int)adler?.Value != readAdler) - { - throw new SharpZipBaseException("Adler chksum doesn't match: " + (int)adler?.Value + " vs. " + readAdler); - } - - mode = FINISHED; - return false; - } - - /// - /// Decodes the deflated stream. - /// - /// - /// false if more input is needed, or if finished. - /// - /// - /// if deflated stream is invalid. - /// - private bool Decode() - { - switch (mode) - { - case DECODE_HEADER: - return DecodeHeader(); - - case DECODE_DICT: - return DecodeDict(); - - case DECODE_CHKSUM: - return DecodeChksum(); - - case DECODE_BLOCKS: - if (isLastBlock) - { - if (noHeader) - { - mode = FINISHED; - return false; - } - else - { - input.SkipToByteBoundary(); - neededBits = 32; - mode = DECODE_CHKSUM; - return true; - } - } - - int type = input.PeekBits(3); - if (type < 0) - { - return false; - } - input.DropBits(3); - - isLastBlock |= (type & 1) != 0; - switch (type >> 1) - { - case DeflaterConstants.STORED_BLOCK: - input.SkipToByteBoundary(); - mode = DECODE_STORED_LEN1; - break; - - case DeflaterConstants.STATIC_TREES: - litlenTree = InflaterHuffmanTree.defLitLenTree; - distTree = InflaterHuffmanTree.defDistTree; - mode = DECODE_HUFFMAN; - break; - - case DeflaterConstants.DYN_TREES: - dynHeader = new InflaterDynHeader(input); - mode = DECODE_DYN_HEADER; - break; - - default: - throw new SharpZipBaseException("Unknown block type " + type); - } - return true; - - case DECODE_STORED_LEN1: - { - if ((uncomprLen = input.PeekBits(16)) < 0) - { - return false; - } - input.DropBits(16); - mode = DECODE_STORED_LEN2; - } - goto case DECODE_STORED_LEN2; // fall through - - case DECODE_STORED_LEN2: - { - int nlen = input.PeekBits(16); - if (nlen < 0) - { - return false; - } - input.DropBits(16); - if (nlen != (uncomprLen ^ 0xffff)) - { - throw new SharpZipBaseException("broken uncompressed block"); - } - mode = DECODE_STORED; - } - goto case DECODE_STORED; // fall through - - case DECODE_STORED: - { - int more = outputWindow.CopyStored(input, uncomprLen); - uncomprLen -= more; - if (uncomprLen == 0) - { - mode = DECODE_BLOCKS; - return true; - } - return !input.IsNeedingInput; - } - - case DECODE_DYN_HEADER: - if (!dynHeader.AttemptRead()) - { - return false; - } - - litlenTree = dynHeader.LiteralLengthTree; - distTree = dynHeader.DistanceTree; - mode = DECODE_HUFFMAN; - goto case DECODE_HUFFMAN; // fall through - - case DECODE_HUFFMAN: - case DECODE_HUFFMAN_LENBITS: - case DECODE_HUFFMAN_DIST: - case DECODE_HUFFMAN_DISTBITS: - return DecodeHuffman(); - - case FINISHED: - return false; - - default: - throw new SharpZipBaseException("Inflater.Decode unknown mode"); - } - } - - /// - /// Sets the preset dictionary. This should only be called, if - /// needsDictionary() returns true and it should set the same - /// dictionary, that was used for deflating. The getAdler() - /// function returns the checksum of the dictionary needed. - /// - /// - /// The dictionary. - /// - public void SetDictionary(byte[] buffer) - { - SetDictionary(buffer, 0, buffer.Length); - } - - /// - /// Sets the preset dictionary. This should only be called, if - /// needsDictionary() returns true and it should set the same - /// dictionary, that was used for deflating. The getAdler() - /// function returns the checksum of the dictionary needed. - /// - /// - /// The dictionary. - /// - /// - /// The index into buffer where the dictionary starts. - /// - /// - /// The number of bytes in the dictionary. - /// - /// - /// No dictionary is needed. - /// - /// - /// The adler checksum for the buffer is invalid - /// - public void SetDictionary(byte[] buffer, int index, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (index < 0) - { - throw new ArgumentOutOfRangeException(nameof(index)); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - if (!IsNeedingDictionary) - { - throw new InvalidOperationException("Dictionary is not needed"); - } - - adler?.Update(new ArraySegment(buffer, index, count)); - - if (adler != null && (int)adler.Value != readAdler) - { - throw new SharpZipBaseException("Wrong adler checksum"); - } - adler?.Reset(); - outputWindow.CopyDict(buffer, index, count); - mode = DECODE_BLOCKS; - } - - /// - /// Sets the input. This should only be called, if needsInput() - /// returns true. - /// - /// - /// the input. - /// - public void SetInput(byte[] buffer) - { - SetInput(buffer, 0, buffer.Length); - } - - /// - /// Sets the input. This should only be called, if needsInput() - /// returns true. - /// - /// - /// The source of input data - /// - /// - /// The index into buffer where the input starts. - /// - /// - /// The number of bytes of input to use. - /// - /// - /// No input is needed. - /// - /// - /// The index and/or count are wrong. - /// - public void SetInput(byte[] buffer, int index, int count) - { - input.SetInput(buffer, index, count); - totalIn += (long)count; - } - - /// - /// Inflates the compressed stream to the output buffer. If this - /// returns 0, you should check, whether IsNeedingDictionary(), - /// IsNeedingInput() or IsFinished() returns true, to determine why no - /// further output is produced. - /// - /// - /// the output buffer. - /// - /// - /// The number of bytes written to the buffer, 0 if no further - /// output can be produced. - /// - /// - /// if buffer has length 0. - /// - /// - /// if deflated stream is invalid. - /// - public int Inflate(byte[] buffer) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - return Inflate(buffer, 0, buffer.Length); - } - - /// - /// Inflates the compressed stream to the output buffer. If this - /// returns 0, you should check, whether needsDictionary(), - /// needsInput() or finished() returns true, to determine why no - /// further output is produced. - /// - /// - /// the output buffer. - /// - /// - /// the offset in buffer where storing starts. - /// - /// - /// the maximum number of bytes to output. - /// - /// - /// the number of bytes written to the buffer, 0 if no further output can be produced. - /// - /// - /// if count is less than 0. - /// - /// - /// if the index and / or count are wrong. - /// - /// - /// if deflated stream is invalid. - /// - public int Inflate(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "count cannot be negative"); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), "offset cannot be negative"); - } - - if (offset + count > buffer.Length) - { - throw new ArgumentException("count exceeds buffer bounds"); - } - - // Special case: count may be zero - if (count == 0) - { - if (!IsFinished) - { // -jr- 08-Nov-2003 INFLATE_BUG fix.. - Decode(); - } - return 0; - } - - int bytesCopied = 0; - - do - { - if (mode != DECODE_CHKSUM) - { - /* Don't give away any output, if we are waiting for the - * checksum in the input stream. - * - * With this trick we have always: - * IsNeedingInput() and not IsFinished() - * implies more output can be produced. - */ - int more = outputWindow.CopyOutput(buffer, offset, count); - if (more > 0) - { - adler?.Update(new ArraySegment(buffer, offset, more)); - offset += more; - bytesCopied += more; - totalOut += (long)more; - count -= more; - if (count == 0) - { - return bytesCopied; - } - } - } - } while (Decode() || ((outputWindow.GetAvailable() > 0) && (mode != DECODE_CHKSUM))); - return bytesCopied; - } - - /// - /// Returns true, if the input buffer is empty. - /// You should then call setInput(). - /// NOTE: This method also returns true when the stream is finished. - /// - public bool IsNeedingInput - { - get - { - return input.IsNeedingInput; - } - } - - /// - /// Returns true, if a preset dictionary is needed to inflate the input. - /// - public bool IsNeedingDictionary - { - get - { - return mode == DECODE_DICT && neededBits == 0; - } - } - - /// - /// Returns true, if the inflater has finished. This means, that no - /// input is needed and no output can be produced. - /// - public bool IsFinished - { - get - { - return mode == FINISHED && outputWindow.GetAvailable() == 0; - } - } - - /// - /// Gets the adler checksum. This is either the checksum of all - /// uncompressed bytes returned by inflate(), or if needsDictionary() - /// returns true (and thus no output was yet produced) this is the - /// adler checksum of the expected dictionary. - /// - /// - /// the adler checksum. - /// - public int Adler - { - get - { - if (IsNeedingDictionary) - { - return readAdler; - } - else if (adler != null) - { - return (int)adler.Value; - } - else - { - return 0; - } - } - } - - /// - /// Gets the total number of output bytes returned by Inflate(). - /// - /// - /// the total number of output bytes. - /// - public long TotalOut - { - get - { - return totalOut; - } - } - - /// - /// Gets the total number of processed compressed input bytes. - /// - /// - /// The total number of bytes of processed input bytes. - /// - public long TotalIn - { - get - { - return totalIn - (long)RemainingInput; - } - } - - /// - /// Gets the number of unprocessed input bytes. Useful, if the end of the - /// stream is reached and you want to further process the bytes after - /// the deflate stream. - /// - /// - /// The number of bytes of the input which have not been processed. - /// - public int RemainingInput - { - // TODO: This should be a long? - get - { - return input.AvailableBytes; - } - } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/InflaterDynHeader.cs b/ICSharpCode.SharpZipLib/Zip/Compression/InflaterDynHeader.cs deleted file mode 100644 index 8e0196b11f71..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/InflaterDynHeader.cs +++ /dev/null @@ -1,151 +0,0 @@ -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; -using System; -using System.Collections.Generic; - -namespace ICSharpCode.SharpZipLib.Zip.Compression -{ - internal class InflaterDynHeader - { - #region Constants - - // maximum number of literal/length codes - private const int LITLEN_MAX = 286; - - // maximum number of distance codes - private const int DIST_MAX = 30; - - // maximum data code lengths to read - private const int CODELEN_MAX = LITLEN_MAX + DIST_MAX; - - // maximum meta code length codes to read - private const int META_MAX = 19; - - private static readonly int[] MetaCodeLengthIndex = - { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - - #endregion Constants - - /// - /// Continue decoding header from until more bits are needed or decoding has been completed - /// - /// Returns whether decoding could be completed - public bool AttemptRead() - => !state.MoveNext() || state.Current; - - public InflaterDynHeader(StreamManipulator input) - { - this.input = input; - stateMachine = CreateStateMachine(); - state = stateMachine.GetEnumerator(); - } - - private IEnumerable CreateStateMachine() - { - // Read initial code length counts from header - while (!input.TryGetBits(5, ref litLenCodeCount, 257)) yield return false; - while (!input.TryGetBits(5, ref distanceCodeCount, 1)) yield return false; - while (!input.TryGetBits(4, ref metaCodeCount, 4)) yield return false; - var dataCodeCount = litLenCodeCount + distanceCodeCount; - - if (litLenCodeCount > LITLEN_MAX) throw new ValueOutOfRangeException(nameof(litLenCodeCount)); - if (distanceCodeCount > DIST_MAX) throw new ValueOutOfRangeException(nameof(distanceCodeCount)); - if (metaCodeCount > META_MAX) throw new ValueOutOfRangeException(nameof(metaCodeCount)); - - // Load code lengths for the meta tree from the header bits - for (int i = 0; i < metaCodeCount; i++) - { - while (!input.TryGetBits(3, ref codeLengths, MetaCodeLengthIndex[i])) yield return false; - } - - var metaCodeTree = new InflaterHuffmanTree(codeLengths); - - // Decompress the meta tree symbols into the data table code lengths - int index = 0; - while (index < dataCodeCount) - { - byte codeLength; - int symbol; - - while ((symbol = metaCodeTree.GetSymbol(input)) < 0) yield return false; - - if (symbol < 16) - { - // append literal code length - codeLengths[index++] = (byte)symbol; - } - else - { - int repeatCount = 0; - - if (symbol == 16) // Repeat last code length 3..6 times - { - if (index == 0) - throw new StreamDecodingException("Cannot repeat previous code length when no other code length has been read"); - - codeLength = codeLengths[index - 1]; - - // 2 bits + 3, [3..6] - while (!input.TryGetBits(2, ref repeatCount, 3)) yield return false; - } - else if (symbol == 17) // Repeat zero 3..10 times - { - codeLength = 0; - - // 3 bits + 3, [3..10] - while (!input.TryGetBits(3, ref repeatCount, 3)) yield return false; - } - else // (symbol == 18), Repeat zero 11..138 times - { - codeLength = 0; - - // 7 bits + 11, [11..138] - while (!input.TryGetBits(7, ref repeatCount, 11)) yield return false; - } - - if (index + repeatCount > dataCodeCount) - throw new StreamDecodingException("Cannot repeat code lengths past total number of data code lengths"); - - while (repeatCount-- > 0) - codeLengths[index++] = codeLength; - } - } - - if (codeLengths[256] == 0) - throw new StreamDecodingException("Inflater dynamic header end-of-block code missing"); - - litLenTree = new InflaterHuffmanTree(new ArraySegment(codeLengths, 0, litLenCodeCount)); - distTree = new InflaterHuffmanTree(new ArraySegment(codeLengths, litLenCodeCount, distanceCodeCount)); - - yield return true; - } - - /// - /// Get literal/length huffman tree, must not be used before has returned true - /// - /// If hader has not been successfully read by the state machine - public InflaterHuffmanTree LiteralLengthTree - => litLenTree ?? throw new StreamDecodingException("Header properties were accessed before header had been successfully read"); - - /// - /// Get distance huffman tree, must not be used before has returned true - /// - /// If hader has not been successfully read by the state machine - public InflaterHuffmanTree DistanceTree - => distTree ?? throw new StreamDecodingException("Header properties were accessed before header had been successfully read"); - - #region Instance Fields - - private readonly StreamManipulator input; - private readonly IEnumerator state; - private readonly IEnumerable stateMachine; - - private byte[] codeLengths = new byte[CODELEN_MAX]; - - private InflaterHuffmanTree litLenTree; - private InflaterHuffmanTree distTree; - - private int litLenCodeCount, distanceCodeCount, metaCodeCount; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/InflaterHuffmanTree.cs b/ICSharpCode.SharpZipLib/Zip/Compression/InflaterHuffmanTree.cs deleted file mode 100644 index ed318824ffe3..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/InflaterHuffmanTree.cs +++ /dev/null @@ -1,237 +0,0 @@ -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; -using System; -using System.Collections.Generic; - -namespace ICSharpCode.SharpZipLib.Zip.Compression -{ - /// - /// Huffman tree used for inflation - /// - public class InflaterHuffmanTree - { - #region Constants - - private const int MAX_BITLEN = 15; - - #endregion Constants - - #region Instance Fields - - private short[] tree; - - #endregion Instance Fields - - /// - /// Literal length tree - /// - public static InflaterHuffmanTree defLitLenTree; - - /// - /// Distance tree - /// - public static InflaterHuffmanTree defDistTree; - - static InflaterHuffmanTree() - { - try - { - byte[] codeLengths = new byte[288]; - int i = 0; - while (i < 144) - { - codeLengths[i++] = 8; - } - while (i < 256) - { - codeLengths[i++] = 9; - } - while (i < 280) - { - codeLengths[i++] = 7; - } - while (i < 288) - { - codeLengths[i++] = 8; - } - defLitLenTree = new InflaterHuffmanTree(codeLengths); - - codeLengths = new byte[32]; - i = 0; - while (i < 32) - { - codeLengths[i++] = 5; - } - defDistTree = new InflaterHuffmanTree(codeLengths); - } - catch (Exception) - { - throw new SharpZipBaseException("InflaterHuffmanTree: static tree length illegal"); - } - } - - #region Constructors - - /// - /// Constructs a Huffman tree from the array of code lengths. - /// - /// - /// the array of code lengths - /// - public InflaterHuffmanTree(IList codeLengths) - { - BuildTree(codeLengths); - } - - #endregion Constructors - - private void BuildTree(IList codeLengths) - { - int[] blCount = new int[MAX_BITLEN + 1]; - int[] nextCode = new int[MAX_BITLEN + 1]; - - for (int i = 0; i < codeLengths.Count; i++) - { - int bits = codeLengths[i]; - if (bits > 0) - { - blCount[bits]++; - } - } - - int code = 0; - int treeSize = 512; - for (int bits = 1; bits <= MAX_BITLEN; bits++) - { - nextCode[bits] = code; - code += blCount[bits] << (16 - bits); - if (bits >= 10) - { - /* We need an extra table for bit lengths >= 10. */ - int start = nextCode[bits] & 0x1ff80; - int end = code & 0x1ff80; - treeSize += (end - start) >> (16 - bits); - } - } - - /* -jr comment this out! doesnt work for dynamic trees and pkzip 2.04g - if (code != 65536) - { - throw new SharpZipBaseException("Code lengths don't add up properly."); - } - */ - /* Now create and fill the extra tables from longest to shortest - * bit len. This way the sub trees will be aligned. - */ - tree = new short[treeSize]; - int treePtr = 512; - for (int bits = MAX_BITLEN; bits >= 10; bits--) - { - int end = code & 0x1ff80; - code -= blCount[bits] << (16 - bits); - int start = code & 0x1ff80; - for (int i = start; i < end; i += 1 << 7) - { - tree[DeflaterHuffman.BitReverse(i)] = (short)((-treePtr << 4) | bits); - treePtr += 1 << (bits - 9); - } - } - - for (int i = 0; i < codeLengths.Count; i++) - { - int bits = codeLengths[i]; - if (bits == 0) - { - continue; - } - code = nextCode[bits]; - int revcode = DeflaterHuffman.BitReverse(code); - if (bits <= 9) - { - do - { - tree[revcode] = (short)((i << 4) | bits); - revcode += 1 << bits; - } while (revcode < 512); - } - else - { - int subTree = tree[revcode & 511]; - int treeLen = 1 << (subTree & 15); - subTree = -(subTree >> 4); - do - { - tree[subTree | (revcode >> 9)] = (short)((i << 4) | bits); - revcode += 1 << bits; - } while (revcode < treeLen); - } - nextCode[bits] = code + (1 << (16 - bits)); - } - } - - /// - /// Reads the next symbol from input. The symbol is encoded using the - /// huffman tree. - /// - /// - /// input the input source. - /// - /// - /// the next symbol, or -1 if not enough input is available. - /// - public int GetSymbol(StreamManipulator input) - { - int lookahead, symbol; - if ((lookahead = input.PeekBits(9)) >= 0) - { - symbol = tree[lookahead]; - int bitlen = symbol & 15; - - if (symbol >= 0) - { - if(bitlen == 0){ - throw new SharpZipBaseException("Encountered invalid codelength 0"); - } - input.DropBits(bitlen); - return symbol >> 4; - } - int subtree = -(symbol >> 4); - if ((lookahead = input.PeekBits(bitlen)) >= 0) - { - symbol = tree[subtree | (lookahead >> 9)]; - input.DropBits(symbol & 15); - return symbol >> 4; - } - else - { - int bits = input.AvailableBits; - lookahead = input.PeekBits(bits); - symbol = tree[subtree | (lookahead >> 9)]; - if ((symbol & 15) <= bits) - { - input.DropBits(symbol & 15); - return symbol >> 4; - } - else - { - return -1; - } - } - } - else // Less than 9 bits - { - int bits = input.AvailableBits; - lookahead = input.PeekBits(bits); - symbol = tree[lookahead]; - if (symbol >= 0 && (symbol & 15) <= bits) - { - input.DropBits(symbol & 15); - return symbol >> 4; - } - else - { - return -1; - } - } - } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/PendingBuffer.cs b/ICSharpCode.SharpZipLib/Zip/Compression/PendingBuffer.cs deleted file mode 100644 index 6ed7e4ab8a72..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/PendingBuffer.cs +++ /dev/null @@ -1,268 +0,0 @@ -namespace ICSharpCode.SharpZipLib.Zip.Compression -{ - /// - /// This class is general purpose class for writing data to a buffer. - /// - /// It allows you to write bits as well as bytes - /// Based on DeflaterPending.java - /// - /// author of the original java version : Jochen Hoenicke - /// - public class PendingBuffer - { - #region Instance Fields - - /// - /// Internal work buffer - /// - private readonly byte[] buffer; - - private int start; - private int end; - - private uint bits; - private int bitCount; - - #endregion Instance Fields - - #region Constructors - - /// - /// construct instance using default buffer size of 4096 - /// - public PendingBuffer() : this(4096) - { - } - - /// - /// construct instance using specified buffer size - /// - /// - /// size to use for internal buffer - /// - public PendingBuffer(int bufferSize) - { - buffer = new byte[bufferSize]; - } - - #endregion Constructors - - /// - /// Clear internal state/buffers - /// - public void Reset() - { - start = end = bitCount = 0; - } - - /// - /// Write a byte to buffer - /// - /// - /// The value to write - /// - public void WriteByte(int value) - { -#if DebugDeflation - if (DeflaterConstants.DEBUGGING && (start != 0) ) - { - throw new SharpZipBaseException("Debug check: start != 0"); - } -#endif - buffer[end++] = unchecked((byte)value); - } - - /// - /// Write a short value to buffer LSB first - /// - /// - /// The value to write. - /// - public void WriteShort(int value) - { -#if DebugDeflation - if (DeflaterConstants.DEBUGGING && (start != 0) ) - { - throw new SharpZipBaseException("Debug check: start != 0"); - } -#endif - buffer[end++] = unchecked((byte)value); - buffer[end++] = unchecked((byte)(value >> 8)); - } - - /// - /// write an integer LSB first - /// - /// The value to write. - public void WriteInt(int value) - { -#if DebugDeflation - if (DeflaterConstants.DEBUGGING && (start != 0) ) - { - throw new SharpZipBaseException("Debug check: start != 0"); - } -#endif - buffer[end++] = unchecked((byte)value); - buffer[end++] = unchecked((byte)(value >> 8)); - buffer[end++] = unchecked((byte)(value >> 16)); - buffer[end++] = unchecked((byte)(value >> 24)); - } - - /// - /// Write a block of data to buffer - /// - /// data to write - /// offset of first byte to write - /// number of bytes to write - public void WriteBlock(byte[] block, int offset, int length) - { -#if DebugDeflation - if (DeflaterConstants.DEBUGGING && (start != 0) ) - { - throw new SharpZipBaseException("Debug check: start != 0"); - } -#endif - System.Array.Copy(block, offset, buffer, end, length); - end += length; - } - - /// - /// The number of bits written to the buffer - /// - public int BitCount - { - get - { - return bitCount; - } - } - - /// - /// Align internal buffer on a byte boundary - /// - public void AlignToByte() - { -#if DebugDeflation - if (DeflaterConstants.DEBUGGING && (start != 0) ) - { - throw new SharpZipBaseException("Debug check: start != 0"); - } -#endif - if (bitCount > 0) - { - buffer[end++] = unchecked((byte)bits); - if (bitCount > 8) - { - buffer[end++] = unchecked((byte)(bits >> 8)); - } - } - bits = 0; - bitCount = 0; - } - - /// - /// Write bits to internal buffer - /// - /// source of bits - /// number of bits to write - public void WriteBits(int b, int count) - { -#if DebugDeflation - if (DeflaterConstants.DEBUGGING && (start != 0) ) - { - throw new SharpZipBaseException("Debug check: start != 0"); - } - - // if (DeflaterConstants.DEBUGGING) { - // //Console.WriteLine("writeBits("+b+","+count+")"); - // } -#endif - bits |= (uint)(b << bitCount); - bitCount += count; - if (bitCount >= 16) - { - buffer[end++] = unchecked((byte)bits); - buffer[end++] = unchecked((byte)(bits >> 8)); - bits >>= 16; - bitCount -= 16; - } - } - - /// - /// Write a short value to internal buffer most significant byte first - /// - /// value to write - public void WriteShortMSB(int s) - { -#if DebugDeflation - if (DeflaterConstants.DEBUGGING && (start != 0) ) - { - throw new SharpZipBaseException("Debug check: start != 0"); - } -#endif - buffer[end++] = unchecked((byte)(s >> 8)); - buffer[end++] = unchecked((byte)s); - } - - /// - /// Indicates if buffer has been flushed - /// - public bool IsFlushed - { - get - { - return end == 0; - } - } - - /// - /// Flushes the pending buffer into the given output array. If the - /// output array is to small, only a partial flush is done. - /// - /// The output array. - /// The offset into output array. - /// The maximum number of bytes to store. - /// The number of bytes flushed. - public int Flush(byte[] output, int offset, int length) - { - if (bitCount >= 8) - { - buffer[end++] = unchecked((byte)bits); - bits >>= 8; - bitCount -= 8; - } - - if (length > end - start) - { - length = end - start; - System.Array.Copy(buffer, start, output, offset, length); - start = 0; - end = 0; - } - else - { - System.Array.Copy(buffer, start, output, offset, length); - start += length; - } - return length; - } - - /// - /// Convert internal buffer to byte array. - /// Buffer is empty on completion - /// - /// - /// The internal buffer contents converted to a byte array. - /// - public byte[] ToByteArray() - { - AlignToByte(); - - byte[] result = new byte[end - start]; - System.Array.Copy(buffer, start, result, 0, result.Length); - start = 0; - end = 0; - return result; - } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs deleted file mode 100644 index 1c54b6848076..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs +++ /dev/null @@ -1,513 +0,0 @@ -using ICSharpCode.SharpZipLib.Encryption; -using System; -using System.IO; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams -{ - /// - /// A special stream deflating or compressing the bytes that are - /// written to it. It uses a Deflater to perform actual deflating.
- /// Authors of the original java version : Tom Tromey, Jochen Hoenicke - ///
- public class DeflaterOutputStream : Stream - { - #region Constructors - - /// - /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size. - /// - /// - /// the output stream where deflated output should be written. - /// - public DeflaterOutputStream(Stream baseOutputStream) - : this(baseOutputStream, new Deflater(), 512) - { - } - - /// - /// Creates a new DeflaterOutputStream with the given Deflater and - /// default buffer size. - /// - /// - /// the output stream where deflated output should be written. - /// - /// - /// the underlying deflater. - /// - public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater) - : this(baseOutputStream, deflater, 512) - { - } - - /// - /// Creates a new DeflaterOutputStream with the given Deflater and - /// buffer size. - /// - /// - /// The output stream where deflated output is written. - /// - /// - /// The underlying deflater to use - /// - /// - /// The buffer size in bytes to use when deflating (minimum value 512) - /// - /// - /// bufsize is less than or equal to zero. - /// - /// - /// baseOutputStream does not support writing - /// - /// - /// deflater instance is null - /// - public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize) - { - if (baseOutputStream == null) - { - throw new ArgumentNullException(nameof(baseOutputStream)); - } - - if (baseOutputStream.CanWrite == false) - { - throw new ArgumentException("Must support writing", nameof(baseOutputStream)); - } - - if (bufferSize < 512) - { - throw new ArgumentOutOfRangeException(nameof(bufferSize)); - } - - baseOutputStream_ = baseOutputStream; - buffer_ = new byte[bufferSize]; - deflater_ = deflater ?? throw new ArgumentNullException(nameof(deflater)); - } - - #endregion Constructors - - #region Public API - - /// - /// Finishes the stream by calling finish() on the deflater. - /// - /// - /// Not all input is deflated - /// - public virtual void Finish() - { - deflater_.Finish(); - while (!deflater_.IsFinished) - { - int len = deflater_.Deflate(buffer_, 0, buffer_.Length); - if (len <= 0) - { - break; - } - - EncryptBlock(buffer_, 0, len); - - baseOutputStream_.Write(buffer_, 0, len); - } - - if (!deflater_.IsFinished) - { - throw new SharpZipBaseException("Can't deflate all input?"); - } - - baseOutputStream_.Flush(); - - if (cryptoTransform_ != null) - { - if (cryptoTransform_ is ZipAESTransform) - { - AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); - } - cryptoTransform_.Dispose(); - cryptoTransform_ = null; - } - } - - /// - /// Finishes the stream by calling finish() on the deflater. - /// - /// The that can be used to cancel the operation. - /// - /// Not all input is deflated - /// - public virtual async Task FinishAsync(CancellationToken ct) - { - deflater_.Finish(); - while (!deflater_.IsFinished) - { - int len = deflater_.Deflate(buffer_, 0, buffer_.Length); - if (len <= 0) - { - break; - } - - EncryptBlock(buffer_, 0, len); - - await baseOutputStream_.WriteAsync(buffer_, 0, len, ct); - } - - if (!deflater_.IsFinished) - { - throw new SharpZipBaseException("Can't deflate all input?"); - } - - await baseOutputStream_.FlushAsync(ct); - - if (cryptoTransform_ != null) - { - if (cryptoTransform_ is ZipAESTransform) - { - AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); - } - cryptoTransform_.Dispose(); - cryptoTransform_ = null; - } - } - - /// - /// Gets or sets a flag indicating ownership of underlying stream. - /// When the flag is true will close the underlying stream also. - /// - /// The default value is true. - public bool IsStreamOwner { get; set; } = true; - - /// - /// Allows client to determine if an entry can be patched after its added - /// - public bool CanPatchEntries - { - get - { - return baseOutputStream_.CanSeek; - } - } - - #endregion Public API - - #region Encryption - - /// - /// The CryptoTransform currently being used to encrypt the compressed data. - /// - protected ICryptoTransform cryptoTransform_; - - /// - /// Returns the 10 byte AUTH CODE to be appended immediately following the AES data stream. - /// - protected byte[] AESAuthCode; - - /// - public Encoding ZipCryptoEncoding { get; set; } = StringCodec.DefaultZipCryptoEncoding; - - /// - /// Encrypt a block of data - /// - /// - /// Data to encrypt. NOTE the original contents of the buffer are lost - /// - /// - /// Offset of first byte in buffer to encrypt - /// - /// - /// Number of bytes in buffer to encrypt - /// - protected void EncryptBlock(byte[] buffer, int offset, int length) - { - if(cryptoTransform_ is null) return; - cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0); - } - - #endregion Encryption - - #region Deflation Support - - /// - /// Deflates everything in the input buffers. This will call - /// def.deflate() until all bytes from the input buffers - /// are processed. - /// - protected void Deflate() - { - Deflate(false); - } - - private void Deflate(bool flushing) - { - while (flushing || !deflater_.IsNeedingInput) - { - int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length); - - if (deflateCount <= 0) - { - break; - } - - EncryptBlock(buffer_, 0, deflateCount); - - baseOutputStream_.Write(buffer_, 0, deflateCount); - } - - if (!deflater_.IsNeedingInput) - { - throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?"); - } - } - - #endregion Deflation Support - - #region Stream Overrides - - /// - /// Gets value indicating stream can be read from - /// - public override bool CanRead - { - get - { - return false; - } - } - - /// - /// Gets a value indicating if seeking is supported for this stream - /// This property always returns false - /// - public override bool CanSeek - { - get - { - return false; - } - } - - /// - /// Get value indicating if this stream supports writing - /// - public override bool CanWrite - { - get - { - return baseOutputStream_.CanWrite; - } - } - - /// - /// Get current length of stream - /// - public override long Length - { - get - { - return baseOutputStream_.Length; - } - } - - /// - /// Gets the current position within the stream. - /// - /// Any attempt to set position - public override long Position - { - get - { - return baseOutputStream_.Position; - } - set - { - throw new NotSupportedException("Position property not supported"); - } - } - - /// - /// Sets the current position of this stream to the given value. Not supported by this class! - /// - /// The offset relative to the to seek. - /// The to seek from. - /// The new position in the stream. - /// Any access - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException("DeflaterOutputStream Seek not supported"); - } - - /// - /// Sets the length of this stream to the given value. Not supported by this class! - /// - /// The new stream length. - /// Any access - public override void SetLength(long value) - { - throw new NotSupportedException("DeflaterOutputStream SetLength not supported"); - } - - /// - /// Read a byte from stream advancing position by one - /// - /// The byte read cast to an int. THe value is -1 if at the end of the stream. - /// Any access - public override int ReadByte() - { - throw new NotSupportedException("DeflaterOutputStream ReadByte not supported"); - } - - /// - /// Read a block of bytes from stream - /// - /// The buffer to store read data in. - /// The offset to start storing at. - /// The maximum number of bytes to read. - /// The actual number of bytes read. Zero if end of stream is detected. - /// Any access - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotSupportedException("DeflaterOutputStream Read not supported"); - } - - /// - /// Flushes the stream by calling Flush on the deflater and then - /// on the underlying stream. This ensures that all bytes are flushed. - /// - public override void Flush() - { - deflater_.Flush(); - Deflate(true); - baseOutputStream_.Flush(); - } - - /// - /// Calls and closes the underlying - /// stream when is true. - /// - protected override void Dispose(bool disposing) - { - if (!isClosed_) - { - isClosed_ = true; - - try - { - Finish(); - if (cryptoTransform_ != null) - { - GetAuthCodeIfAES(); - cryptoTransform_.Dispose(); - cryptoTransform_ = null; - } - } - finally - { - if (IsStreamOwner) - { - baseOutputStream_.Dispose(); - } - } - } - } - -#if NETSTANDARD2_1 - /// - /// Calls and closes the underlying - /// stream when is true. - /// - public override async ValueTask DisposeAsync() - { - if (!isClosed_) - { - isClosed_ = true; - - try - { - await FinishAsync(CancellationToken.None); - if (cryptoTransform_ != null) - { - GetAuthCodeIfAES(); - cryptoTransform_.Dispose(); - cryptoTransform_ = null; - } - } - finally - { - if (IsStreamOwner) - { - await baseOutputStream_.DisposeAsync(); - } - } - } - } -#endif - - /// - /// Get the Auth code for AES encrypted entries - /// - protected void GetAuthCodeIfAES() - { - if (cryptoTransform_ is ZipAESTransform) - { - AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); - } - } - - /// - /// Writes a single byte to the compressed output stream. - /// - /// - /// The byte value. - /// - public override void WriteByte(byte value) - { - byte[] b = new byte[1]; - b[0] = value; - Write(b, 0, 1); - } - - /// - /// Writes bytes from an array to the compressed stream. - /// - /// - /// The byte array - /// - /// - /// The offset into the byte array where to start. - /// - /// - /// The number of bytes to write. - /// - public override void Write(byte[] buffer, int offset, int count) - { - deflater_.SetInput(buffer, offset, count); - Deflate(); - } - - #endregion Stream Overrides - - #region Instance Fields - - /// - /// This buffer is used temporarily to retrieve the bytes from the - /// deflater and write them to the underlying output stream. - /// - private byte[] buffer_; - - /// - /// The deflater which is used to deflate the stream. - /// - protected Deflater deflater_; - - /// - /// Base stream the deflater depends on. - /// - protected Stream baseOutputStream_; - - private bool isClosed_; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs deleted file mode 100644 index 7790474d24a6..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs +++ /dev/null @@ -1,713 +0,0 @@ -using System; -using System.IO; -using System.Security.Cryptography; - -namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams -{ - /// - /// An input buffer customised for use by - /// - /// - /// The buffer supports decryption of incoming data. - /// - public class InflaterInputBuffer - { - #region Constructors - - /// - /// Initialise a new instance of with a default buffer size - /// - /// The stream to buffer. - public InflaterInputBuffer(Stream stream) : this(stream, 4096) - { - } - - /// - /// Initialise a new instance of - /// - /// The stream to buffer. - /// The size to use for the buffer - /// A minimum buffer size of 1KB is permitted. Lower sizes are treated as 1KB. - public InflaterInputBuffer(Stream stream, int bufferSize) - { - inputStream = stream; - if (bufferSize < 1024) - { - bufferSize = 1024; - } - rawData = new byte[bufferSize]; - clearText = rawData; - } - - #endregion Constructors - - /// - /// Get the length of bytes in the - /// - public int RawLength - { - get - { - return rawLength; - } - } - - /// - /// Get the contents of the raw data buffer. - /// - /// This may contain encrypted data. - public byte[] RawData - { - get - { - return rawData; - } - } - - /// - /// Get the number of useable bytes in - /// - public int ClearTextLength - { - get - { - return clearTextLength; - } - } - - /// - /// Get the contents of the clear text buffer. - /// - public byte[] ClearText - { - get - { - return clearText; - } - } - - /// - /// Get/set the number of bytes available - /// - public int Available - { - get { return available; } - set { available = value; } - } - - /// - /// Call passing the current clear text buffer contents. - /// - /// The inflater to set input for. - public void SetInflaterInput(Inflater inflater) - { - if (available > 0) - { - inflater.SetInput(clearText, clearTextLength - available, available); - available = 0; - } - } - - /// - /// Fill the buffer from the underlying input stream. - /// - public void Fill() - { - rawLength = 0; - int toRead = rawData.Length; - - while (toRead > 0 && inputStream.CanRead) - { - int count = inputStream.Read(rawData, rawLength, toRead); - if (count <= 0) - { - break; - } - rawLength += count; - toRead -= count; - } - - if (cryptoTransform != null) - { - clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0); - } - else - { - clearTextLength = rawLength; - } - - available = clearTextLength; - } - - /// - /// Read a buffer directly from the input stream - /// - /// The buffer to fill - /// Returns the number of bytes read. - public int ReadRawBuffer(byte[] buffer) - { - return ReadRawBuffer(buffer, 0, buffer.Length); - } - - /// - /// Read a buffer directly from the input stream - /// - /// The buffer to read into - /// The offset to start reading data into. - /// The number of bytes to read. - /// Returns the number of bytes read. - public int ReadRawBuffer(byte[] outBuffer, int offset, int length) - { - if (length < 0) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - - int currentOffset = offset; - int currentLength = length; - - while (currentLength > 0) - { - if (available <= 0) - { - Fill(); - if (available <= 0) - { - return 0; - } - } - int toCopy = Math.Min(currentLength, available); - System.Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy); - currentOffset += toCopy; - currentLength -= toCopy; - available -= toCopy; - } - return length; - } - - /// - /// Read clear text data from the input stream. - /// - /// The buffer to add data to. - /// The offset to start adding data at. - /// The number of bytes to read. - /// Returns the number of bytes actually read. - public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length) - { - if (length < 0) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - - int currentOffset = offset; - int currentLength = length; - - while (currentLength > 0) - { - if (available <= 0) - { - Fill(); - if (available <= 0) - { - return 0; - } - } - - int toCopy = Math.Min(currentLength, available); - Array.Copy(clearText, clearTextLength - (int)available, outBuffer, currentOffset, toCopy); - currentOffset += toCopy; - currentLength -= toCopy; - available -= toCopy; - } - return length; - } - - /// - /// Read a from the input stream. - /// - /// Returns the byte read. - public byte ReadLeByte() - { - if (available <= 0) - { - Fill(); - if (available <= 0) - { - throw new ZipException("EOF in header"); - } - } - byte result = rawData[rawLength - available]; - available -= 1; - return result; - } - - /// - /// Read an in little endian byte order. - /// - /// The short value read case to an int. - public int ReadLeShort() - { - return ReadLeByte() | (ReadLeByte() << 8); - } - - /// - /// Read an in little endian byte order. - /// - /// The int value read. - public int ReadLeInt() - { - return ReadLeShort() | (ReadLeShort() << 16); - } - - /// - /// Read a in little endian byte order. - /// - /// The long value read. - public long ReadLeLong() - { - return (uint)ReadLeInt() | ((long)ReadLeInt() << 32); - } - - /// - /// Get/set the to apply to any data. - /// - /// Set this value to null to have no transform applied. - public ICryptoTransform CryptoTransform - { - set - { - cryptoTransform = value; - if (cryptoTransform != null) - { - if (rawData == clearText) - { - if (internalClearText == null) - { - internalClearText = new byte[rawData.Length]; - } - clearText = internalClearText; - } - clearTextLength = rawLength; - if (available > 0) - { - cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available); - } - } - else - { - clearText = rawData; - clearTextLength = rawLength; - } - } - } - - #region Instance Fields - - private int rawLength; - private byte[] rawData; - - private int clearTextLength; - private byte[] clearText; - private byte[] internalClearText; - - private int available; - - private ICryptoTransform cryptoTransform; - private Stream inputStream; - - #endregion Instance Fields - } - - /// - /// This filter stream is used to decompress data compressed using the "deflate" - /// format. The "deflate" format is described in RFC 1951. - /// - /// This stream may form the basis for other decompression filters, such - /// as the GZipInputStream. - /// - /// Author of the original java version : John Leuner. - /// - public class InflaterInputStream : Stream - { - #region Constructors - - /// - /// Create an InflaterInputStream with the default decompressor - /// and a default buffer size of 4KB. - /// - /// - /// The InputStream to read bytes from - /// - public InflaterInputStream(Stream baseInputStream) - : this(baseInputStream, new Inflater(), 4096) - { - } - - /// - /// Create an InflaterInputStream with the specified decompressor - /// and a default buffer size of 4KB. - /// - /// - /// The source of input data - /// - /// - /// The decompressor used to decompress data read from baseInputStream - /// - public InflaterInputStream(Stream baseInputStream, Inflater inf) - : this(baseInputStream, inf, 4096) - { - } - - /// - /// Create an InflaterInputStream with the specified decompressor - /// and the specified buffer size. - /// - /// - /// The InputStream to read bytes from - /// - /// - /// The decompressor to use - /// - /// - /// Size of the buffer to use - /// - public InflaterInputStream(Stream baseInputStream, Inflater inflater, int bufferSize) - { - if (baseInputStream == null) - { - throw new ArgumentNullException(nameof(baseInputStream)); - } - - if (inflater == null) - { - throw new ArgumentNullException(nameof(inflater)); - } - - if (bufferSize <= 0) - { - throw new ArgumentOutOfRangeException(nameof(bufferSize)); - } - - this.baseInputStream = baseInputStream; - this.inf = inflater; - - inputBuffer = new InflaterInputBuffer(baseInputStream, bufferSize); - } - - #endregion Constructors - - /// - /// Gets or sets a flag indicating ownership of underlying stream. - /// When the flag is true will close the underlying stream also. - /// - /// The default value is true. - public bool IsStreamOwner { get; set; } = true; - - /// - /// Skip specified number of bytes of uncompressed data - /// - /// - /// Number of bytes to skip - /// - /// - /// The number of bytes skipped, zero if the end of - /// stream has been reached - /// - /// - /// The number of bytes to skip is less than or equal to zero. - /// - public long Skip(long count) - { - if (count <= 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - // v0.80 Skip by seeking if underlying stream supports it... - if (baseInputStream.CanSeek) - { - baseInputStream.Seek(count, SeekOrigin.Current); - return count; - } - else - { - int length = 2048; - if (count < length) - { - length = (int)count; - } - - byte[] tmp = new byte[length]; - int readCount = 1; - long toSkip = count; - - while ((toSkip > 0) && (readCount > 0)) - { - if (toSkip < length) - { - length = (int)toSkip; - } - - readCount = baseInputStream.Read(tmp, 0, length); - toSkip -= readCount; - } - - return count - toSkip; - } - } - - /// - /// Clear any cryptographic state. - /// - protected void StopDecrypting() - { - inputBuffer.CryptoTransform = null; - } - - /// - /// Returns 0 once the end of the stream (EOF) has been reached. - /// Otherwise returns 1. - /// - public virtual int Available - { - get - { - return inf.IsFinished ? 0 : 1; - } - } - - /// - /// Fills the buffer with more data to decompress. - /// - /// - /// Stream ends early - /// - protected void Fill() - { - // Protect against redundant calls - if (inputBuffer.Available <= 0) - { - inputBuffer.Fill(); - if (inputBuffer.Available <= 0) - { - throw new SharpZipBaseException("Unexpected EOF"); - } - } - inputBuffer.SetInflaterInput(inf); - } - - #region Stream Overrides - - /// - /// Gets a value indicating whether the current stream supports reading - /// - public override bool CanRead - { - get - { - return baseInputStream.CanRead; - } - } - - /// - /// Gets a value of false indicating seeking is not supported for this stream. - /// - public override bool CanSeek - { - get - { - return false; - } - } - - /// - /// Gets a value of false indicating that this stream is not writeable. - /// - public override bool CanWrite - { - get - { - return false; - } - } - - /// - /// A value representing the length of the stream in bytes. - /// - public override long Length - { - get - { - //return inputBuffer.RawLength; - throw new NotSupportedException("InflaterInputStream Length is not supported"); - } - } - - /// - /// The current position within the stream. - /// Throws a NotSupportedException when attempting to set the position - /// - /// Attempting to set the position - public override long Position - { - get - { - return baseInputStream.Position; - } - set - { - throw new NotSupportedException("InflaterInputStream Position not supported"); - } - } - - /// - /// Flushes the baseInputStream - /// - public override void Flush() - { - baseInputStream.Flush(); - } - - /// - /// Sets the position within the current stream - /// Always throws a NotSupportedException - /// - /// The relative offset to seek to. - /// The defining where to seek from. - /// The new position in the stream. - /// Any access - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException("Seek not supported"); - } - - /// - /// Set the length of the current stream - /// Always throws a NotSupportedException - /// - /// The new length value for the stream. - /// Any access - public override void SetLength(long value) - { - throw new NotSupportedException("InflaterInputStream SetLength not supported"); - } - - /// - /// Writes a sequence of bytes to stream and advances the current position - /// This method always throws a NotSupportedException - /// - /// The buffer containing data to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// Any access - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException("InflaterInputStream Write not supported"); - } - - /// - /// Writes one byte to the current stream and advances the current position - /// Always throws a NotSupportedException - /// - /// The byte to write. - /// Any access - public override void WriteByte(byte value) - { - throw new NotSupportedException("InflaterInputStream WriteByte not supported"); - } - - /// - /// Closes the input stream. When - /// is true the underlying stream is also closed. - /// - protected override void Dispose(bool disposing) - { - if (!isClosed) - { - isClosed = true; - if (IsStreamOwner) - { - baseInputStream.Dispose(); - } - } - } - - /// - /// Reads decompressed data into the provided buffer byte array - /// - /// - /// The array to read and decompress data into - /// - /// - /// The offset indicating where the data should be placed - /// - /// - /// The number of bytes to decompress - /// - /// The number of bytes read. Zero signals the end of stream - /// - /// Inflater needs a dictionary - /// - public override int Read(byte[] buffer, int offset, int count) - { - if (inf.IsNeedingDictionary) - { - throw new SharpZipBaseException("Need a dictionary"); - } - - int remainingBytes = count; - while (true) - { - int bytesRead = inf.Inflate(buffer, offset, remainingBytes); - offset += bytesRead; - remainingBytes -= bytesRead; - - if (remainingBytes == 0 || inf.IsFinished) - { - break; - } - - if (inf.IsNeedingInput) - { - Fill(); - } - else if (bytesRead == 0) - { - throw new ZipException("Invalid input data"); - } - } - return count - remainingBytes; - } - - #endregion Stream Overrides - - #region Instance Fields - - /// - /// Decompressor for this stream - /// - protected Inflater inf; - - /// - /// Input buffer for this stream. - /// - protected InflaterInputBuffer inputBuffer; - - /// - /// Base stream the inflater reads from. - /// - private Stream baseInputStream; - - /// - /// The compressed size - /// - protected long csize; - - /// - /// Flag indicating whether this instance has been closed or not. - /// - private bool isClosed; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/OutputWindow.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/OutputWindow.cs deleted file mode 100644 index d8241c18c200..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/OutputWindow.cs +++ /dev/null @@ -1,220 +0,0 @@ -using System; - -namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams -{ - /// - /// Contains the output from the Inflation process. - /// We need to have a window so that we can refer backwards into the output stream - /// to repeat stuff.
- /// Author of the original java version : John Leuner - ///
- public class OutputWindow - { - #region Constants - - private const int WindowSize = 1 << 15; - private const int WindowMask = WindowSize - 1; - - #endregion Constants - - #region Instance Fields - - private byte[] window = new byte[WindowSize]; //The window is 2^15 bytes - private int windowEnd; - private int windowFilled; - - #endregion Instance Fields - - /// - /// Write a byte to this output window - /// - /// value to write - /// - /// if window is full - /// - public void Write(int value) - { - if (windowFilled++ == WindowSize) - { - throw new InvalidOperationException("Window full"); - } - window[windowEnd++] = (byte)value; - windowEnd &= WindowMask; - } - - private void SlowRepeat(int repStart, int length, int distance) - { - while (length-- > 0) - { - window[windowEnd++] = window[repStart++]; - windowEnd &= WindowMask; - repStart &= WindowMask; - } - } - - /// - /// Append a byte pattern already in the window itself - /// - /// length of pattern to copy - /// distance from end of window pattern occurs - /// - /// If the repeated data overflows the window - /// - public void Repeat(int length, int distance) - { - if ((windowFilled += length) > WindowSize) - { - throw new InvalidOperationException("Window full"); - } - - int repStart = (windowEnd - distance) & WindowMask; - int border = WindowSize - length; - if ((repStart <= border) && (windowEnd < border)) - { - if (length <= distance) - { - System.Array.Copy(window, repStart, window, windowEnd, length); - windowEnd += length; - } - else - { - // We have to copy manually, since the repeat pattern overlaps. - while (length-- > 0) - { - window[windowEnd++] = window[repStart++]; - } - } - } - else - { - SlowRepeat(repStart, length, distance); - } - } - - /// - /// Copy from input manipulator to internal window - /// - /// source of data - /// length of data to copy - /// the number of bytes copied - public int CopyStored(StreamManipulator input, int length) - { - length = Math.Min(Math.Min(length, WindowSize - windowFilled), input.AvailableBytes); - int copied; - - int tailLen = WindowSize - windowEnd; - if (length > tailLen) - { - copied = input.CopyBytes(window, windowEnd, tailLen); - if (copied == tailLen) - { - copied += input.CopyBytes(window, 0, length - tailLen); - } - } - else - { - copied = input.CopyBytes(window, windowEnd, length); - } - - windowEnd = (windowEnd + copied) & WindowMask; - windowFilled += copied; - return copied; - } - - /// - /// Copy dictionary to window - /// - /// source dictionary - /// offset of start in source dictionary - /// length of dictionary - /// - /// If window isnt empty - /// - public void CopyDict(byte[] dictionary, int offset, int length) - { - if (dictionary == null) - { - throw new ArgumentNullException(nameof(dictionary)); - } - - if (windowFilled > 0) - { - throw new InvalidOperationException(); - } - - if (length > WindowSize) - { - offset += length - WindowSize; - length = WindowSize; - } - System.Array.Copy(dictionary, offset, window, 0, length); - windowEnd = length & WindowMask; - } - - /// - /// Get remaining unfilled space in window - /// - /// Number of bytes left in window - public int GetFreeSpace() - { - return WindowSize - windowFilled; - } - - /// - /// Get bytes available for output in window - /// - /// Number of bytes filled - public int GetAvailable() - { - return windowFilled; - } - - /// - /// Copy contents of window to output - /// - /// buffer to copy to - /// offset to start at - /// number of bytes to count - /// The number of bytes copied - /// - /// If a window underflow occurs - /// - public int CopyOutput(byte[] output, int offset, int len) - { - int copyEnd = windowEnd; - if (len > windowFilled) - { - len = windowFilled; - } - else - { - copyEnd = (windowEnd - windowFilled + len) & WindowMask; - } - - int copied = len; - int tailLen = len - copyEnd; - - if (tailLen > 0) - { - System.Array.Copy(window, WindowSize - tailLen, output, offset, tailLen); - offset += tailLen; - len = copyEnd; - } - System.Array.Copy(window, copyEnd - len, output, offset, len); - windowFilled -= copied; - if (windowFilled < 0) - { - throw new InvalidOperationException(); - } - return copied; - } - - /// - /// Reset by clearing window so GetAvailable returns 0 - /// - public void Reset() - { - windowFilled = windowEnd = 0; - } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/StreamManipulator.cs b/ICSharpCode.SharpZipLib/Zip/Compression/Streams/StreamManipulator.cs deleted file mode 100644 index aff6a9c6c525..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/Compression/Streams/StreamManipulator.cs +++ /dev/null @@ -1,298 +0,0 @@ -using System; - -namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams -{ - /// - /// This class allows us to retrieve a specified number of bits from - /// the input buffer, as well as copy big byte blocks. - /// - /// It uses an int buffer to store up to 31 bits for direct - /// manipulation. This guarantees that we can get at least 16 bits, - /// but we only need at most 15, so this is all safe. - /// - /// There are some optimizations in this class, for example, you must - /// never peek more than 8 bits more than needed, and you must first - /// peek bits before you may drop them. This is not a general purpose - /// class but optimized for the behaviour of the Inflater. - /// - /// authors of the original java version : John Leuner, Jochen Hoenicke - /// - public class StreamManipulator - { - /// - /// Get the next sequence of bits but don't increase input pointer. bitCount must be - /// less or equal 16 and if this call succeeds, you must drop - /// at least n - 8 bits in the next call. - /// - /// The number of bits to peek. - /// - /// the value of the bits, or -1 if not enough bits available. */ - /// - public int PeekBits(int bitCount) - { - if (bitsInBuffer_ < bitCount) - { - if (windowStart_ == windowEnd_) - { - return -1; // ok - } - buffer_ |= (uint)((window_[windowStart_++] & 0xff | - (window_[windowStart_++] & 0xff) << 8) << bitsInBuffer_); - bitsInBuffer_ += 16; - } - return (int)(buffer_ & ((1 << bitCount) - 1)); - } - - /// - /// Tries to grab the next bits from the input and - /// sets to the value, adding . - /// - /// true if enough bits could be read, otherwise false - public bool TryGetBits(int bitCount, ref int output, int outputOffset = 0) - { - var bits = PeekBits(bitCount); - if (bits < 0) - { - return false; - } - output = bits + outputOffset; - DropBits(bitCount); - return true; - } - - /// - /// Tries to grab the next bits from the input and - /// sets of to the value. - /// - /// true if enough bits could be read, otherwise false - public bool TryGetBits(int bitCount, ref byte[] array, int index) - { - var bits = PeekBits(bitCount); - if (bits < 0) - { - return false; - } - array[index] = (byte)bits; - DropBits(bitCount); - return true; - } - - /// - /// Drops the next n bits from the input. You should have called PeekBits - /// with a bigger or equal n before, to make sure that enough bits are in - /// the bit buffer. - /// - /// The number of bits to drop. - public void DropBits(int bitCount) - { - buffer_ >>= bitCount; - bitsInBuffer_ -= bitCount; - } - - /// - /// Gets the next n bits and increases input pointer. This is equivalent - /// to followed by , except for correct error handling. - /// - /// The number of bits to retrieve. - /// - /// the value of the bits, or -1 if not enough bits available. - /// - public int GetBits(int bitCount) - { - int bits = PeekBits(bitCount); - if (bits >= 0) - { - DropBits(bitCount); - } - return bits; - } - - /// - /// Gets the number of bits available in the bit buffer. This must be - /// only called when a previous PeekBits() returned -1. - /// - /// - /// the number of bits available. - /// - public int AvailableBits - { - get - { - return bitsInBuffer_; - } - } - - /// - /// Gets the number of bytes available. - /// - /// - /// The number of bytes available. - /// - public int AvailableBytes - { - get - { - return windowEnd_ - windowStart_ + (bitsInBuffer_ >> 3); - } - } - - /// - /// Skips to the next byte boundary. - /// - public void SkipToByteBoundary() - { - buffer_ >>= (bitsInBuffer_ & 7); - bitsInBuffer_ &= ~7; - } - - /// - /// Returns true when SetInput can be called - /// - public bool IsNeedingInput - { - get - { - return windowStart_ == windowEnd_; - } - } - - /// - /// Copies 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. - /// - /// - /// The buffer to copy bytes to. - /// - /// - /// The offset in the buffer at which copying starts - /// - /// - /// The length to copy, 0 is allowed. - /// - /// - /// The number of bytes copied, 0 if no bytes were available. - /// - /// - /// Length is less than zero - /// - /// - /// Bit buffer isnt byte aligned - /// - public int CopyBytes(byte[] output, int offset, int length) - { - if (length < 0) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - - if ((bitsInBuffer_ & 7) != 0) - { - // bits_in_buffer may only be 0 or a multiple of 8 - throw new InvalidOperationException("Bit buffer is not byte aligned!"); - } - - int count = 0; - while ((bitsInBuffer_ > 0) && (length > 0)) - { - output[offset++] = (byte)buffer_; - buffer_ >>= 8; - bitsInBuffer_ -= 8; - length--; - count++; - } - - if (length == 0) - { - return count; - } - - int avail = windowEnd_ - windowStart_; - if (length > avail) - { - length = avail; - } - System.Array.Copy(window_, windowStart_, output, offset, length); - windowStart_ += length; - - if (((windowStart_ - windowEnd_) & 1) != 0) - { - // We always want an even number of bytes in input, see peekBits - buffer_ = (uint)(window_[windowStart_++] & 0xff); - bitsInBuffer_ = 8; - } - return count + length; - } - - /// - /// Resets state and empties internal buffers - /// - public void Reset() - { - buffer_ = 0; - windowStart_ = windowEnd_ = bitsInBuffer_ = 0; - } - - /// - /// Add more input for consumption. - /// Only call when IsNeedingInput returns true - /// - /// data to be input - /// offset of first byte of input - /// number of bytes of input to add. - public void SetInput(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative"); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative"); - } - - if (windowStart_ < windowEnd_) - { - throw new InvalidOperationException("Old input was not completely processed"); - } - - int end = offset + count; - - // We want to throw an ArrayIndexOutOfBoundsException early. - // Note the check also handles integer wrap around. - if ((offset > end) || (end > buffer.Length)) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - if ((count & 1) != 0) - { - // We always want an even number of bytes in input, see PeekBits - buffer_ |= (uint)((buffer[offset++] & 0xff) << bitsInBuffer_); - bitsInBuffer_ += 8; - } - - window_ = buffer; - windowStart_ = offset; - windowEnd_ = end; - } - - #region Instance Fields - - private byte[] window_; - private int windowStart_; - private int windowEnd_; - - private uint buffer_; - private int bitsInBuffer_; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/FastZip.cs b/ICSharpCode.SharpZipLib/Zip/FastZip.cs deleted file mode 100644 index 13aedb02101d..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/FastZip.cs +++ /dev/null @@ -1,1003 +0,0 @@ -using ICSharpCode.SharpZipLib.Core; -using ICSharpCode.SharpZipLib.Zip.Compression; -using System; -using System.IO; -using static ICSharpCode.SharpZipLib.Zip.Compression.Deflater; -using static ICSharpCode.SharpZipLib.Zip.ZipEntryFactory; - -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// FastZipEvents supports all events applicable to FastZip operations. - /// - public class FastZipEvents - { - /// - /// Delegate to invoke when processing directories. - /// - public event EventHandler ProcessDirectory; - - /// - /// Delegate to invoke when processing files. - /// - public ProcessFileHandler ProcessFile; - - /// - /// Delegate to invoke during processing of files. - /// - public ProgressHandler Progress; - - /// - /// Delegate to invoke when processing for a file has been completed. - /// - public CompletedFileHandler CompletedFile; - - /// - /// Delegate to invoke when processing directory failures. - /// - public DirectoryFailureHandler DirectoryFailure; - - /// - /// Delegate to invoke when processing file failures. - /// - public FileFailureHandler FileFailure; - - /// - /// Raise the directory failure event. - /// - /// The directory causing the failure. - /// The exception for this event. - /// A boolean indicating if execution should continue or not. - public bool OnDirectoryFailure(string directory, Exception e) - { - bool result = false; - DirectoryFailureHandler handler = DirectoryFailure; - - if (handler != null) - { - var args = new ScanFailureEventArgs(directory, e); - handler(this, args); - result = args.ContinueRunning; - } - return result; - } - - /// - /// Fires the file failure handler delegate. - /// - /// The file causing the failure. - /// The exception for this failure. - /// A boolean indicating if execution should continue or not. - public bool OnFileFailure(string file, Exception e) - { - FileFailureHandler handler = FileFailure; - bool result = (handler != null); - - if (result) - { - var args = new ScanFailureEventArgs(file, e); - handler(this, args); - result = args.ContinueRunning; - } - return result; - } - - /// - /// Fires the ProcessFile delegate. - /// - /// The file being processed. - /// A boolean indicating if execution should continue or not. - public bool OnProcessFile(string file) - { - bool result = true; - ProcessFileHandler handler = ProcessFile; - - if (handler != null) - { - var args = new ScanEventArgs(file); - handler(this, args); - result = args.ContinueRunning; - } - return result; - } - - /// - /// Fires the delegate - /// - /// The file whose processing has been completed. - /// A boolean indicating if execution should continue or not. - public bool OnCompletedFile(string file) - { - bool result = true; - CompletedFileHandler handler = CompletedFile; - if (handler != null) - { - var args = new ScanEventArgs(file); - handler(this, args); - result = args.ContinueRunning; - } - return result; - } - - /// - /// Fires the process directory delegate. - /// - /// The directory being processed. - /// Flag indicating if the directory has matching files as determined by the current filter. - /// A of true if the operation should continue; false otherwise. - public bool OnProcessDirectory(string directory, bool hasMatchingFiles) - { - bool result = true; - EventHandler handler = ProcessDirectory; - if (handler != null) - { - var args = new DirectoryEventArgs(directory, hasMatchingFiles); - handler(this, args); - result = args.ContinueRunning; - } - return result; - } - - /// - /// The minimum timespan between events. - /// - /// The minimum period of time between events. - /// - /// The default interval is three seconds. - public TimeSpan ProgressInterval - { - get { return progressInterval_; } - set { progressInterval_ = value; } - } - - #region Instance Fields - - private TimeSpan progressInterval_ = TimeSpan.FromSeconds(3); - - #endregion Instance Fields - } - - /// - /// FastZip provides facilities for creating and extracting zip files. - /// - public class FastZip - { - #region Enumerations - - /// - /// Defines the desired handling when overwriting files during extraction. - /// - public enum Overwrite - { - /// - /// Prompt the user to confirm overwriting - /// - Prompt, - - /// - /// Never overwrite files. - /// - Never, - - /// - /// Always overwrite files. - /// - Always - } - - #endregion Enumerations - - #region Constructors - - /// - /// Initialise a default instance of . - /// - public FastZip() - { - } - - /// - /// Initialise a new instance of using the specified - /// - /// The time setting to use when creating or extracting Zip entries. - /// Using TimeSetting.LastAccessTime[Utc] when - /// creating an archive will set the file time to the moment of reading. - /// - public FastZip(TimeSetting timeSetting) - { - entryFactory_ = new ZipEntryFactory(timeSetting); - restoreDateTimeOnExtract_ = true; - } - - /// - /// Initialise a new instance of using the specified - /// - /// The time to set all values for created or extracted Zip Entries. - public FastZip(DateTime time) - { - entryFactory_ = new ZipEntryFactory(time); - restoreDateTimeOnExtract_ = true; - } - - /// - /// Initialise a new instance of - /// - /// The events to use during operations. - public FastZip(FastZipEvents events) - { - events_ = events; - } - - #endregion Constructors - - #region Properties - - /// - /// Get/set a value indicating whether empty directories should be created. - /// - public bool CreateEmptyDirectories - { - get { return createEmptyDirectories_; } - set { createEmptyDirectories_ = value; } - } - - /// - /// Get / set the password value. - /// - public string Password - { - get { return password_; } - set { password_ = value; } - } - - /// - /// Get / set the method of encrypting entries. - /// - /// - /// Only applies when is set. - /// Defaults to ZipCrypto for backwards compatibility purposes. - /// - public ZipEncryptionMethod EntryEncryptionMethod { get; set; } = ZipEncryptionMethod.ZipCrypto; - - /// - /// Get or set the active when creating Zip files. - /// - /// - public INameTransform NameTransform - { - get { return entryFactory_.NameTransform; } - set - { - entryFactory_.NameTransform = value; - } - } - - /// - /// Get or set the active when creating Zip files. - /// - public IEntryFactory EntryFactory - { - get { return entryFactory_; } - set - { - if (value == null) - { - entryFactory_ = new ZipEntryFactory(); - } - else - { - entryFactory_ = value; - } - } - } - - /// - /// Gets or sets the setting for Zip64 handling when writing. - /// - /// - /// The default value is dynamic which is not backwards compatible with old - /// programs and can cause problems with XP's built in compression which cant - /// read Zip64 archives. However it does avoid the situation were a large file - /// is added and cannot be completed correctly. - /// NOTE: Setting the size for entries before they are added is the best solution! - /// By default the EntryFactory used by FastZip will set the file size. - /// - public UseZip64 UseZip64 - { - get { return useZip64_; } - set { useZip64_ = value; } - } - - /// - /// Get/set a value indicating whether file dates and times should - /// be restored when extracting files from an archive. - /// - /// The default value is false. - public bool RestoreDateTimeOnExtract - { - get - { - return restoreDateTimeOnExtract_; - } - set - { - restoreDateTimeOnExtract_ = value; - } - } - - /// - /// Get/set a value indicating whether file attributes should - /// be restored during extract operations - /// - public bool RestoreAttributesOnExtract - { - get { return restoreAttributesOnExtract_; } - set { restoreAttributesOnExtract_ = value; } - } - - /// - /// Get/set the Compression Level that will be used - /// when creating the zip - /// - public Deflater.CompressionLevel CompressionLevel - { - get { return compressionLevel_; } - set { compressionLevel_ = value; } - } - - /// - /// Reflects the opposite of the internal , setting it to false overrides the encoding used for reading and writing zip entries - /// - public bool UseUnicode - { - get => !_stringCodec.ForceZipLegacyEncoding; - set => _stringCodec.ForceZipLegacyEncoding = !value; - } - - /// Gets or sets the code page used for reading/writing zip file entries when unicode is disabled - public int LegacyCodePage - { - get => _stringCodec.CodePage; - set => _stringCodec.CodePage = value; - } - - /// - public StringCodec StringCodec - { - get => _stringCodec; - set => _stringCodec = value; - } - - #endregion Properties - - #region Delegates - - /// - /// Delegate called when confirming overwriting of files. - /// - public delegate bool ConfirmOverwriteDelegate(string fileName); - - #endregion Delegates - - #region CreateZip - - /// - /// Create a zip file. - /// - /// The name of the zip file to create. - /// The directory to source files from. - /// True to recurse directories, false for no recursion. - /// The file filter to apply. - /// The directory filter to apply. - public void CreateZip(string zipFileName, string sourceDirectory, - bool recurse, string fileFilter, string directoryFilter) - { - CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, directoryFilter); - } - - /// - /// Create a zip file/archive. - /// - /// The name of the zip file to create. - /// The directory to obtain files and directories from. - /// True to recurse directories, false for no recursion. - /// The file filter to apply. - public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter) - { - CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, null); - } - - /// - /// Create a zip archive sending output to the passed. - /// - /// The stream to write archive data to. - /// The directory to source files from. - /// True to recurse directories, false for no recursion. - /// The file filter to apply. - /// The directory filter to apply. - /// The is closed after creation. - public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter) - { - CreateZip(outputStream, sourceDirectory, recurse, fileFilter, directoryFilter, false); - } - - /// - /// Create a zip archive sending output to the passed. - /// - /// The stream to write archive data to. - /// The directory to source files from. - /// True to recurse directories, false for no recursion. - /// The file filter to apply. - /// The directory filter to apply. - /// true to leave open after the zip has been created, false to dispose it. - public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter, bool leaveOpen) - { - var scanner = new FileSystemScanner(fileFilter, directoryFilter); - CreateZip(outputStream, sourceDirectory, recurse, scanner, leaveOpen); - } - - /// - /// Create a zip file. - /// - /// The name of the zip file to create. - /// The directory to source files from. - /// True to recurse directories, false for no recursion. - /// The file filter to apply. - /// The directory filter to apply. - public void CreateZip(string zipFileName, string sourceDirectory, - bool recurse, IScanFilter fileFilter, IScanFilter directoryFilter) - { - CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, directoryFilter, false); - } - - /// - /// Create a zip archive sending output to the passed. - /// - /// The stream to write archive data to. - /// The directory to source files from. - /// True to recurse directories, false for no recursion. - /// The file filter to apply. - /// The directory filter to apply. - /// true to leave open after the zip has been created, false to dispose it. - public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, IScanFilter fileFilter, IScanFilter directoryFilter, bool leaveOpen = false) - { - var scanner = new FileSystemScanner(fileFilter, directoryFilter); - CreateZip(outputStream, sourceDirectory, recurse, scanner, leaveOpen); - } - - /// - /// Create a zip archive sending output to the passed. - /// - /// The stream to write archive data to. - /// The directory to source files from. - /// True to recurse directories, false for no recursion. - /// For performing the actual file system scan - /// true to leave open after the zip has been created, false to dispose it. - /// The is closed after creation. - private void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, FileSystemScanner scanner, bool leaveOpen) - { - NameTransform = new ZipNameTransform(sourceDirectory); - sourceDirectory_ = sourceDirectory; - - using (outputStream_ = new ZipOutputStream(outputStream, _stringCodec)) - { - outputStream_.SetLevel((int)CompressionLevel); - outputStream_.IsStreamOwner = !leaveOpen; - outputStream_.NameTransform = null; // all required transforms handled by us - - if (false == string.IsNullOrEmpty(password_) && EntryEncryptionMethod != ZipEncryptionMethod.None) - { - outputStream_.Password = password_; - } - - outputStream_.UseZip64 = UseZip64; - scanner.ProcessFile += ProcessFile; - if (this.CreateEmptyDirectories) - { - scanner.ProcessDirectory += ProcessDirectory; - } - - if (events_ != null) - { - if (events_.FileFailure != null) - { - scanner.FileFailure += events_.FileFailure; - } - - if (events_.DirectoryFailure != null) - { - scanner.DirectoryFailure += events_.DirectoryFailure; - } - } - - scanner.Scan(sourceDirectory, recurse); - } - } - - #endregion CreateZip - - #region ExtractZip - - /// - /// Extract the contents of a zip file. - /// - /// The zip file to extract from. - /// The directory to save extracted information in. - /// A filter to apply to files. - public void ExtractZip(string zipFileName, string targetDirectory, string fileFilter) - { - ExtractZip(zipFileName, targetDirectory, Overwrite.Always, null, fileFilter, null, restoreDateTimeOnExtract_); - } - - /// - /// Extract the contents of a zip file. - /// - /// The zip file to extract from. - /// The directory to save extracted information in. - /// The style of overwriting to apply. - /// A delegate to invoke when confirming overwriting. - /// A filter to apply to files. - /// A filter to apply to directories. - /// Flag indicating whether to restore the date and time for extracted files. - /// Allow parent directory traversal in file paths (e.g. ../file) - public void ExtractZip(string zipFileName, string targetDirectory, - Overwrite overwrite, ConfirmOverwriteDelegate confirmDelegate, - string fileFilter, string directoryFilter, bool restoreDateTime, bool allowParentTraversal = false) - { - Stream inputStream = File.Open(zipFileName, FileMode.Open, FileAccess.Read, FileShare.Read); - ExtractZip(inputStream, targetDirectory, overwrite, confirmDelegate, fileFilter, directoryFilter, restoreDateTime, true, allowParentTraversal); - } - - /// - /// Extract the contents of a zip file held in a stream. - /// - /// The seekable input stream containing the zip to extract from. - /// The directory to save extracted information in. - /// The style of overwriting to apply. - /// A delegate to invoke when confirming overwriting. - /// A filter to apply to files. - /// A filter to apply to directories. - /// Flag indicating whether to restore the date and time for extracted files. - /// Flag indicating whether the inputStream will be closed by this method. - /// Allow parent directory traversal in file paths (e.g. ../file) - public void ExtractZip(Stream inputStream, string targetDirectory, - Overwrite overwrite, ConfirmOverwriteDelegate confirmDelegate, - string fileFilter, string directoryFilter, bool restoreDateTime, - bool isStreamOwner, bool allowParentTraversal = false) - { - if ((overwrite == Overwrite.Prompt) && (confirmDelegate == null)) - { - throw new ArgumentNullException(nameof(confirmDelegate)); - } - - continueRunning_ = true; - overwrite_ = overwrite; - confirmDelegate_ = confirmDelegate; - extractNameTransform_ = new WindowsNameTransform(targetDirectory, allowParentTraversal); - - fileFilter_ = new NameFilter(fileFilter); - directoryFilter_ = new NameFilter(directoryFilter); - restoreDateTimeOnExtract_ = restoreDateTime; - - using (zipFile_ = new ZipFile(inputStream, !isStreamOwner)) - { - if (password_ != null) - { - zipFile_.Password = password_; - } - - System.Collections.IEnumerator enumerator = zipFile_.GetEnumerator(); - while (continueRunning_ && enumerator.MoveNext()) - { - var entry = (ZipEntry)enumerator.Current; - if (entry.IsFile) - { - // TODO Path.GetDirectory can fail here on invalid characters. - if (directoryFilter_.IsMatch(Path.GetDirectoryName(entry.Name)) && fileFilter_.IsMatch(entry.Name)) - { - ExtractEntry(entry); - } - } - else if (entry.IsDirectory) - { - if (directoryFilter_.IsMatch(entry.Name) && CreateEmptyDirectories) - { - ExtractEntry(entry); - } - } - else - { - // Do nothing for volume labels etc... - } - } - } - } - - #endregion ExtractZip - - #region Internal Processing - - private void ProcessDirectory(object sender, DirectoryEventArgs e) - { - if (!e.HasMatchingFiles && CreateEmptyDirectories) - { - if (events_ != null) - { - events_.OnProcessDirectory(e.Name, e.HasMatchingFiles); - } - - if (e.ContinueRunning) - { - if (e.Name != sourceDirectory_) - { - ZipEntry entry = entryFactory_.MakeDirectoryEntry(e.Name); - outputStream_.PutNextEntry(entry); - } - } - } - } - - private void ProcessFile(object sender, ScanEventArgs e) - { - if ((events_ != null) && (events_.ProcessFile != null)) - { - events_.ProcessFile(sender, e); - } - - if (e.ContinueRunning) - { - try - { - // The open below is equivalent to OpenRead which guarantees that if opened the - // file will not be changed by subsequent openers, but precludes opening in some cases - // were it could succeed. ie the open may fail as its already open for writing and the share mode should reflect that. - using (FileStream stream = File.Open(e.Name, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - ZipEntry entry = entryFactory_.MakeFileEntry(e.Name); - if (_stringCodec.ForceZipLegacyEncoding) - { - entry.IsUnicodeText = false; - } - - // Set up AES encryption for the entry if required. - ConfigureEntryEncryption(entry); - - outputStream_.PutNextEntry(entry); - AddFileContents(e.Name, stream); - } - } - catch (Exception ex) - { - if (events_ != null) - { - continueRunning_ = events_.OnFileFailure(e.Name, ex); - } - else - { - continueRunning_ = false; - throw; - } - } - } - } - - // Set up the encryption method to use for the specific entry. - private void ConfigureEntryEncryption(ZipEntry entry) - { - // Only alter the entries options if AES isn't already enabled for it - // (it might have been set up by the entry factory, and if so we let that take precedence) - if (!string.IsNullOrEmpty(Password) && entry.AESEncryptionStrength == 0) - { - switch (EntryEncryptionMethod) - { - case ZipEncryptionMethod.AES128: - entry.AESKeySize = 128; - break; - - case ZipEncryptionMethod.AES256: - entry.AESKeySize = 256; - break; - } - } - } - - private void AddFileContents(string name, Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (buffer_ == null) - { - buffer_ = new byte[4096]; - } - - if ((events_ != null) && (events_.Progress != null)) - { - StreamUtils.Copy(stream, outputStream_, buffer_, - events_.Progress, events_.ProgressInterval, this, name); - } - else - { - StreamUtils.Copy(stream, outputStream_, buffer_); - } - - if (events_ != null) - { - continueRunning_ = events_.OnCompletedFile(name); - } - } - - private void ExtractFileEntry(ZipEntry entry, string targetName) - { - bool proceed = true; - if (overwrite_ != Overwrite.Always) - { - if (File.Exists(targetName)) - { - if ((overwrite_ == Overwrite.Prompt) && (confirmDelegate_ != null)) - { - proceed = confirmDelegate_(targetName); - } - else - { - proceed = false; - } - } - } - - if (proceed) - { - if (events_ != null) - { - continueRunning_ = events_.OnProcessFile(entry.Name); - } - - if (continueRunning_) - { - try - { - using (FileStream outputStream = File.Create(targetName)) - { - if (buffer_ == null) - { - buffer_ = new byte[4096]; - } - - using (var inputStream = zipFile_.GetInputStream(entry)) - { - if ((events_ != null) && (events_.Progress != null)) - { - StreamUtils.Copy(inputStream, outputStream, buffer_, - events_.Progress, events_.ProgressInterval, this, entry.Name, entry.Size); - } - else - { - StreamUtils.Copy(inputStream, outputStream, buffer_); - } - } - - if (events_ != null) - { - continueRunning_ = events_.OnCompletedFile(entry.Name); - } - } - - if (restoreDateTimeOnExtract_) - { - switch (entryFactory_.Setting) - { - case TimeSetting.CreateTime: - File.SetCreationTime(targetName, entry.DateTime); - break; - - case TimeSetting.CreateTimeUtc: - File.SetCreationTimeUtc(targetName, entry.DateTime); - break; - - case TimeSetting.LastAccessTime: - File.SetLastAccessTime(targetName, entry.DateTime); - break; - - case TimeSetting.LastAccessTimeUtc: - File.SetLastAccessTimeUtc(targetName, entry.DateTime); - break; - - case TimeSetting.LastWriteTime: - File.SetLastWriteTime(targetName, entry.DateTime); - break; - - case TimeSetting.LastWriteTimeUtc: - File.SetLastWriteTimeUtc(targetName, entry.DateTime); - break; - - case TimeSetting.Fixed: - File.SetLastWriteTime(targetName, entryFactory_.FixedDateTime); - break; - - default: - throw new ZipException("Unhandled time setting in ExtractFileEntry"); - } - } - - if (RestoreAttributesOnExtract && entry.IsDOSEntry && (entry.ExternalFileAttributes != -1)) - { - var fileAttributes = (FileAttributes)entry.ExternalFileAttributes; - // TODO: FastZip - Setting of other file attributes on extraction is a little trickier. - fileAttributes &= (FileAttributes.Archive | FileAttributes.Normal | FileAttributes.ReadOnly | FileAttributes.Hidden); - File.SetAttributes(targetName, fileAttributes); - } - } - catch (Exception ex) - { - if (events_ != null) - { - continueRunning_ = events_.OnFileFailure(targetName, ex); - } - else - { - continueRunning_ = false; - throw; - } - } - } - } - } - - private void ExtractEntry(ZipEntry entry) - { - bool doExtraction = entry.IsCompressionMethodSupported(); - string targetName = entry.Name; - - if (doExtraction) - { - if (entry.IsFile) - { - targetName = extractNameTransform_.TransformFile(targetName); - } - else if (entry.IsDirectory) - { - targetName = extractNameTransform_.TransformDirectory(targetName); - } - - doExtraction = !(string.IsNullOrEmpty(targetName)); - } - - // TODO: Fire delegate/throw exception were compression method not supported, or name is invalid? - - string dirName = string.Empty; - - if (doExtraction) - { - if (entry.IsDirectory) - { - dirName = targetName; - } - else - { - dirName = Path.GetDirectoryName(Path.GetFullPath(targetName)); - } - } - - if (doExtraction && !Directory.Exists(dirName)) - { - if (!entry.IsDirectory || CreateEmptyDirectories) - { - try - { - continueRunning_ = events_?.OnProcessDirectory(dirName, true) ?? true; - if (continueRunning_) - { - Directory.CreateDirectory(dirName); - if (entry.IsDirectory && restoreDateTimeOnExtract_) - { - switch (entryFactory_.Setting) - { - case TimeSetting.CreateTime: - Directory.SetCreationTime(dirName, entry.DateTime); - break; - - case TimeSetting.CreateTimeUtc: - Directory.SetCreationTimeUtc(dirName, entry.DateTime); - break; - - case TimeSetting.LastAccessTime: - Directory.SetLastAccessTime(dirName, entry.DateTime); - break; - - case TimeSetting.LastAccessTimeUtc: - Directory.SetLastAccessTimeUtc(dirName, entry.DateTime); - break; - - case TimeSetting.LastWriteTime: - Directory.SetLastWriteTime(dirName, entry.DateTime); - break; - - case TimeSetting.LastWriteTimeUtc: - Directory.SetLastWriteTimeUtc(dirName, entry.DateTime); - break; - - case TimeSetting.Fixed: - Directory.SetLastWriteTime(dirName, entryFactory_.FixedDateTime); - break; - - default: - throw new ZipException("Unhandled time setting in ExtractEntry"); - } - } - } - else - { - doExtraction = false; - } - } - catch (Exception ex) - { - doExtraction = false; - if (events_ != null) - { - if (entry.IsDirectory) - { - continueRunning_ = events_.OnDirectoryFailure(targetName, ex); - } - else - { - continueRunning_ = events_.OnFileFailure(targetName, ex); - } - } - else - { - continueRunning_ = false; - throw; - } - } - } - } - - if (doExtraction && entry.IsFile) - { - ExtractFileEntry(entry, targetName); - } - } - - private static int MakeExternalAttributes(FileInfo info) - { - return (int)info.Attributes; - } - - private static bool NameIsValid(string name) - { - return !string.IsNullOrEmpty(name) && - (name.IndexOfAny(Path.GetInvalidPathChars()) < 0); - } - - #endregion Internal Processing - - #region Instance Fields - - private bool continueRunning_; - private byte[] buffer_; - private ZipOutputStream outputStream_; - private ZipFile zipFile_; - private string sourceDirectory_; - private NameFilter fileFilter_; - private NameFilter directoryFilter_; - private Overwrite overwrite_; - private ConfirmOverwriteDelegate confirmDelegate_; - - private bool restoreDateTimeOnExtract_; - private bool restoreAttributesOnExtract_; - private bool createEmptyDirectories_; - private FastZipEvents events_; - private IEntryFactory entryFactory_ = new ZipEntryFactory(); - private INameTransform extractNameTransform_; - private UseZip64 useZip64_ = UseZip64.Dynamic; - private CompressionLevel compressionLevel_ = CompressionLevel.DEFAULT_COMPRESSION; - private StringCodec _stringCodec = new StringCodec(); - - private string password_; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs b/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs deleted file mode 100644 index d7ec181405ef..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/IEntryFactory.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using ICSharpCode.SharpZipLib.Core; -using static ICSharpCode.SharpZipLib.Zip.ZipEntryFactory; - -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// Defines factory methods for creating new values. - /// - public interface IEntryFactory - { - /// - /// Create a for a file given its name - /// - /// The name of the file to create an entry for. - /// Returns a file entry based on the passed. - ZipEntry MakeFileEntry(string fileName); - - /// - /// Create a for a file given its name - /// - /// The name of the file to create an entry for. - /// If true get details from the file system if the file exists. - /// Returns a file entry based on the passed. - ZipEntry MakeFileEntry(string fileName, bool useFileSystem); - - /// - /// Create a for a file given its actual name and optional override name - /// - /// The name of the file to create an entry for. - /// An alternative name to be used for the new entry. Null if not applicable. - /// If true get details from the file system if the file exists. - /// Returns a file entry based on the passed. - ZipEntry MakeFileEntry(string fileName, string entryName, bool useFileSystem); - - /// - /// Create a for a directory given its name - /// - /// The name of the directory to create an entry for. - /// Returns a directory entry based on the passed. - ZipEntry MakeDirectoryEntry(string directoryName); - - /// - /// Create a for a directory given its name - /// - /// The name of the directory to create an entry for. - /// If true get details from the file system for this directory if it exists. - /// Returns a directory entry based on the passed. - ZipEntry MakeDirectoryEntry(string directoryName, bool useFileSystem); - - /// - /// Get/set the applicable. - /// - INameTransform NameTransform { get; set; } - - /// - /// Get the in use. - /// - TimeSetting Setting { get; } - - /// - /// Get the value to use when is set to , - /// or if not specified, the value of when the class was the initialized - /// - DateTime FixedDateTime { get; } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/WindowsNameTransform.cs b/ICSharpCode.SharpZipLib/Zip/WindowsNameTransform.cs deleted file mode 100644 index 43aa61403634..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/WindowsNameTransform.cs +++ /dev/null @@ -1,266 +0,0 @@ -using ICSharpCode.SharpZipLib.Core; -using System; -using System.IO; -using System.Runtime.InteropServices; -using System.Text; - -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// WindowsNameTransform transforms names to windows compatible ones. - /// - public class WindowsNameTransform : INameTransform - { - /// - /// The maximum windows path name permitted. - /// - /// This may not valid for all windows systems - CE?, etc but I cant find the equivalent in the CLR. - private const int MaxPath = 260; - - private string _baseDirectory; - private bool _trimIncomingPaths; - private char _replacementChar = '_'; - private bool _allowParentTraversal; - - /// - /// In this case we need Windows' invalid path characters. - /// Path.GetInvalidPathChars() only returns a subset invalid on all platforms. - /// - private static readonly char[] InvalidEntryChars = new char[] { - '"', '<', '>', '|', '\0', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', - '\u0006', '\a', '\b', '\t', '\n', '\v', '\f', '\r', '\u000e', '\u000f', - '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015', '\u0016', - '\u0017', '\u0018', '\u0019', '\u001a', '\u001b', '\u001c', '\u001d', - '\u001e', '\u001f', - // extra characters for masks, etc. - '*', '?', ':' - }; - - /// - /// Initialises a new instance of - /// - /// - /// Allow parent directory traversal in file paths (e.g. ../file) - public WindowsNameTransform(string baseDirectory, bool allowParentTraversal = false) - { - BaseDirectory = baseDirectory ?? throw new ArgumentNullException(nameof(baseDirectory), "Directory name is invalid"); - AllowParentTraversal = allowParentTraversal; - } - - /// - /// Initialise a default instance of - /// - public WindowsNameTransform() - { - // Do nothing. - } - - /// - /// Gets or sets a value containing the target directory to prefix values with. - /// - public string BaseDirectory - { - get { return _baseDirectory; } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - _baseDirectory = Path.GetFullPath(value); - } - } - - /// - /// Allow parent directory traversal in file paths (e.g. ../file) - /// - public bool AllowParentTraversal - { - get => _allowParentTraversal; - set => _allowParentTraversal = value; - } - - /// - /// Gets or sets a value indicating whether paths on incoming values should be removed. - /// - public bool TrimIncomingPaths - { - get { return _trimIncomingPaths; } - set { _trimIncomingPaths = value; } - } - - /// - /// Transform a Zip directory name to a windows directory name. - /// - /// The directory name to transform. - /// The transformed name. - public string TransformDirectory(string name) - { - name = TransformFile(name); - if (name.Length > 0) - { - while (name.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) - { - name = name.Remove(name.Length - 1, 1); - } - } - else - { - throw new InvalidNameException("Cannot have an empty directory name"); - } - return name; - } - - /// - /// Transform a Zip format file name to a windows style one. - /// - /// The file name to transform. - /// The transformed name. - public string TransformFile(string name) - { - if (name != null) - { - name = MakeValidName(name, _replacementChar); - - if (_trimIncomingPaths) - { - name = Path.GetFileName(name); - } - - // This may exceed windows length restrictions. - // Combine will throw a PathTooLongException in that case. - if (_baseDirectory != null) - { - name = Path.Combine(_baseDirectory, name); - - // Ensure base directory ends with directory separator ('/' or '\' depending on OS) - var pathBase = Path.GetFullPath(_baseDirectory); - if (pathBase[pathBase.Length - 1] != Path.DirectorySeparatorChar) - { - pathBase += Path.DirectorySeparatorChar; - } - - if (!_allowParentTraversal && !Path.GetFullPath(name).StartsWith(pathBase, StringComparison.InvariantCultureIgnoreCase)) - { - throw new InvalidNameException("Parent traversal in paths is not allowed"); - } - } - } - else - { - name = string.Empty; - } - return name; - } - - /// - /// Test a name to see if it is a valid name for a windows filename as extracted from a Zip archive. - /// - /// The name to test. - /// Returns true if the name is a valid zip name; false otherwise. - /// The filename isnt a true windows path in some fundamental ways like no absolute paths, no rooted paths etc. - public static bool IsValidName(string name) - { - bool result = - (name != null) && - (name.Length <= MaxPath) && - (string.Compare(name, MakeValidName(name, '_'), StringComparison.Ordinal) == 0) - ; - - return result; - } - - /// - /// Force a name to be valid by replacing invalid characters with a fixed value - /// - /// The name to make valid - /// The replacement character to use for any invalid characters. - /// Returns a valid name - public static string MakeValidName(string name, char replacement) - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - name = PathUtils.DropPathRoot(name.Replace("/", Path.DirectorySeparatorChar.ToString())); - - // Drop any leading slashes. - while ((name.Length > 0) && (name[0] == Path.DirectorySeparatorChar)) - { - name = name.Remove(0, 1); - } - - // Drop any trailing slashes. - while ((name.Length > 0) && (name[name.Length - 1] == Path.DirectorySeparatorChar)) - { - name = name.Remove(name.Length - 1, 1); - } - - // Convert consecutive \\ characters to \ - int index = name.IndexOf(string.Format("{0}{0}", Path.DirectorySeparatorChar), StringComparison.Ordinal); - while (index >= 0) - { - name = name.Remove(index, 1); - index = name.IndexOf(string.Format("{0}{0}", Path.DirectorySeparatorChar), StringComparison.Ordinal); - } - - // Convert any invalid characters using the replacement one. - index = name.IndexOfAny(InvalidEntryChars); - if (index >= 0) - { - var builder = new StringBuilder(name); - - while (index >= 0) - { - builder[index] = replacement; - - if (index >= name.Length) - { - index = -1; - } - else - { - index = name.IndexOfAny(InvalidEntryChars, index + 1); - } - } - name = builder.ToString(); - } - - // Check for names greater than MaxPath characters. - // TODO: Were is CLR version of MaxPath defined? Can't find it in Environment. - if (name.Length > MaxPath) - { - throw new PathTooLongException(); - } - - return name; - } - - /// - /// Gets or set the character to replace invalid characters during transformations. - /// - public char Replacement - { - get { return _replacementChar; } - set - { - for (int i = 0; i < InvalidEntryChars.Length; ++i) - { - if (InvalidEntryChars[i] == value) - { - throw new ArgumentException("invalid path character"); - } - } - - if ((value == Path.DirectorySeparatorChar) || (value == Path.AltDirectorySeparatorChar)) - { - throw new ArgumentException("invalid replacement character"); - } - - _replacementChar = value; - } - } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipConstants.cs b/ICSharpCode.SharpZipLib/Zip/ZipConstants.cs deleted file mode 100644 index 6d4892d55035..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipConstants.cs +++ /dev/null @@ -1,475 +0,0 @@ -using System; - -namespace ICSharpCode.SharpZipLib.Zip -{ - #region Enumerations - - /// - /// Determines how entries are tested to see if they should use Zip64 extensions or not. - /// - public enum UseZip64 - { - /// - /// Zip64 will not be forced on entries during processing. - /// - /// An entry can have this overridden if required - Off, - - /// - /// Zip64 should always be used. - /// - On, - - /// - /// #ZipLib will determine use based on entry values when added to archive. - /// - Dynamic, - } - - /// - /// The kind of compression used for an entry in an archive - /// - public enum CompressionMethod - { - /// - /// A direct copy of the file contents is held in the archive - /// - Stored = 0, - - /// - /// Common Zip compression method using a sliding dictionary - /// of up to 32KB and secondary compression from Huffman/Shannon-Fano trees - /// - Deflated = 8, - - /// - /// An extension to deflate with a 64KB window. Not supported by #Zip currently - /// - Deflate64 = 9, - - /// - /// BZip2 compression. Not supported by #Zip. - /// - BZip2 = 12, - - /// - /// LZMA compression. Not supported by #Zip. - /// - LZMA = 14, - - /// - /// PPMd compression. Not supported by #Zip. - /// - PPMd = 98, - - /// - /// WinZip special for AES encryption, Now supported by #Zip. - /// - WinZipAES = 99, - } - - /// - /// Identifies the encryption algorithm used for an entry - /// - public enum EncryptionAlgorithm - { - /// - /// No encryption has been used. - /// - None = 0, - - /// - /// Encrypted using PKZIP 2.0 or 'classic' encryption. - /// - PkzipClassic = 1, - - /// - /// DES encryption has been used. - /// - Des = 0x6601, - - /// - /// RC2 encryption has been used for encryption. - /// - RC2 = 0x6602, - - /// - /// Triple DES encryption with 168 bit keys has been used for this entry. - /// - TripleDes168 = 0x6603, - - /// - /// Triple DES with 112 bit keys has been used for this entry. - /// - TripleDes112 = 0x6609, - - /// - /// AES 128 has been used for encryption. - /// - Aes128 = 0x660e, - - /// - /// AES 192 has been used for encryption. - /// - Aes192 = 0x660f, - - /// - /// AES 256 has been used for encryption. - /// - Aes256 = 0x6610, - - /// - /// RC2 corrected has been used for encryption. - /// - RC2Corrected = 0x6702, - - /// - /// Blowfish has been used for encryption. - /// - Blowfish = 0x6720, - - /// - /// Twofish has been used for encryption. - /// - Twofish = 0x6721, - - /// - /// RC4 has been used for encryption. - /// - RC4 = 0x6801, - - /// - /// An unknown algorithm has been used for encryption. - /// - Unknown = 0xffff - } - - /// - /// Defines the contents of the general bit flags field for an archive entry. - /// - [Flags] - public enum GeneralBitFlags - { - /// - /// Bit 0 if set indicates that the file is encrypted - /// - Encrypted = 0x0001, - - /// - /// Bits 1 and 2 - Two bits defining the compression method (only for Method 6 Imploding and 8,9 Deflating) - /// - Method = 0x0006, - - /// - /// Bit 3 if set indicates a trailing data descriptor is appended to the entry data - /// - Descriptor = 0x0008, - - /// - /// Bit 4 is reserved for use with method 8 for enhanced deflation - /// - ReservedPKware4 = 0x0010, - - /// - /// Bit 5 if set indicates the file contains Pkzip compressed patched data. - /// Requires version 2.7 or greater. - /// - Patched = 0x0020, - - /// - /// Bit 6 if set indicates strong encryption has been used for this entry. - /// - StrongEncryption = 0x0040, - - /// - /// Bit 7 is currently unused - /// - Unused7 = 0x0080, - - /// - /// Bit 8 is currently unused - /// - Unused8 = 0x0100, - - /// - /// Bit 9 is currently unused - /// - Unused9 = 0x0200, - - /// - /// Bit 10 is currently unused - /// - Unused10 = 0x0400, - - /// - /// Bit 11 if set indicates the filename and - /// comment fields for this file must be encoded using UTF-8. - /// - UnicodeText = 0x0800, - - /// - /// Bit 12 is documented as being reserved by PKware for enhanced compression. - /// - EnhancedCompress = 0x1000, - - /// - /// Bit 13 if set indicates that values in the local header are masked to hide - /// their actual values, and the central directory is encrypted. - /// - /// - /// Used when encrypting the central directory contents. - /// - HeaderMasked = 0x2000, - - /// - /// Bit 14 is documented as being reserved for use by PKware - /// - ReservedPkware14 = 0x4000, - - /// - /// Bit 15 is documented as being reserved for use by PKware - /// - ReservedPkware15 = 0x8000 - } - - #endregion Enumerations - - /// - /// This class contains constants used for Zip format files - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "kept for backwards compatibility")] - public static class ZipConstants - { - #region Versions - - /// - /// The version made by field for entries in the central header when created by this library - /// - /// - /// This is also the Zip version for the library when comparing against the version required to extract - /// for an entry. See . - /// - public const int VersionMadeBy = 51; // was 45 before AES - - /// - /// The version made by field for entries in the central header when created by this library - /// - /// - /// This is also the Zip version for the library when comparing against the version required to extract - /// for an entry. See ZipInputStream.CanDecompressEntry. - /// - [Obsolete("Use VersionMadeBy instead")] - public const int VERSION_MADE_BY = 51; - - /// - /// The minimum version required to support strong encryption - /// - public const int VersionStrongEncryption = 50; - - /// - /// The minimum version required to support strong encryption - /// - [Obsolete("Use VersionStrongEncryption instead")] - public const int VERSION_STRONG_ENCRYPTION = 50; - - /// - /// Version indicating AES encryption - /// - public const int VERSION_AES = 51; - - /// - /// The version required for Zip64 extensions (4.5 or higher) - /// - public const int VersionZip64 = 45; - - /// - /// The version required for BZip2 compression (4.6 or higher) - /// - public const int VersionBZip2 = 46; - - #endregion Versions - - #region Header Sizes - - /// - /// Size of local entry header (excluding variable length fields at end) - /// - public const int LocalHeaderBaseSize = 30; - - /// - /// Size of local entry header (excluding variable length fields at end) - /// - [Obsolete("Use LocalHeaderBaseSize instead")] - public const int LOCHDR = 30; - - /// - /// Size of Zip64 data descriptor - /// - public const int Zip64DataDescriptorSize = 24; - - /// - /// Size of data descriptor - /// - public const int DataDescriptorSize = 16; - - /// - /// Size of data descriptor - /// - [Obsolete("Use DataDescriptorSize instead")] - public const int EXTHDR = 16; - - /// - /// Size of central header entry (excluding variable fields) - /// - public const int CentralHeaderBaseSize = 46; - - /// - /// Size of central header entry - /// - [Obsolete("Use CentralHeaderBaseSize instead")] - public const int CENHDR = 46; - - /// - /// Size of end of central record (excluding variable fields) - /// - public const int EndOfCentralRecordBaseSize = 22; - - /// - /// Size of end of central record (excluding variable fields) - /// - [Obsolete("Use EndOfCentralRecordBaseSize instead")] - public const int ENDHDR = 22; - - /// - /// Size of 'classic' cryptographic header stored before any entry data - /// - public const int CryptoHeaderSize = 12; - - /// - /// Size of cryptographic header stored before entry data - /// - [Obsolete("Use CryptoHeaderSize instead")] - public const int CRYPTO_HEADER_SIZE = 12; - - /// - /// The size of the Zip64 central directory locator. - /// - public const int Zip64EndOfCentralDirectoryLocatorSize = 20; - - #endregion Header Sizes - - #region Header Signatures - - /// - /// Signature for local entry header - /// - public const int LocalHeaderSignature = 'P' | ('K' << 8) | (3 << 16) | (4 << 24); - - /// - /// Signature for local entry header - /// - [Obsolete("Use LocalHeaderSignature instead")] - public const int LOCSIG = 'P' | ('K' << 8) | (3 << 16) | (4 << 24); - - /// - /// Signature for spanning entry - /// - public const int SpanningSignature = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); - - /// - /// Signature for spanning entry - /// - [Obsolete("Use SpanningSignature instead")] - public const int SPANNINGSIG = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); - - /// - /// Signature for temporary spanning entry - /// - public const int SpanningTempSignature = 'P' | ('K' << 8) | ('0' << 16) | ('0' << 24); - - /// - /// Signature for temporary spanning entry - /// - [Obsolete("Use SpanningTempSignature instead")] - public const int SPANTEMPSIG = 'P' | ('K' << 8) | ('0' << 16) | ('0' << 24); - - /// - /// Signature for data descriptor - /// - /// - /// This is only used where the length, Crc, or compressed size isnt known when the - /// entry is created and the output stream doesnt support seeking. - /// The local entry cannot be 'patched' with the correct values in this case - /// so the values are recorded after the data prefixed by this header, as well as in the central directory. - /// - public const int DataDescriptorSignature = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); - - /// - /// Signature for data descriptor - /// - /// - /// This is only used where the length, Crc, or compressed size isnt known when the - /// entry is created and the output stream doesnt support seeking. - /// The local entry cannot be 'patched' with the correct values in this case - /// so the values are recorded after the data prefixed by this header, as well as in the central directory. - /// - [Obsolete("Use DataDescriptorSignature instead")] - public const int EXTSIG = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); - - /// - /// Signature for central header - /// - [Obsolete("Use CentralHeaderSignature instead")] - public const int CENSIG = 'P' | ('K' << 8) | (1 << 16) | (2 << 24); - - /// - /// Signature for central header - /// - public const int CentralHeaderSignature = 'P' | ('K' << 8) | (1 << 16) | (2 << 24); - - /// - /// Signature for Zip64 central file header - /// - public const int Zip64CentralFileHeaderSignature = 'P' | ('K' << 8) | (6 << 16) | (6 << 24); - - /// - /// Signature for Zip64 central file header - /// - [Obsolete("Use Zip64CentralFileHeaderSignature instead")] - public const int CENSIG64 = 'P' | ('K' << 8) | (6 << 16) | (6 << 24); - - /// - /// Signature for Zip64 central directory locator - /// - public const int Zip64CentralDirLocatorSignature = 'P' | ('K' << 8) | (6 << 16) | (7 << 24); - - /// - /// Signature for archive extra data signature (were headers are encrypted). - /// - public const int ArchiveExtraDataSignature = 'P' | ('K' << 8) | (6 << 16) | (7 << 24); - - /// - /// Central header digital signature - /// - public const int CentralHeaderDigitalSignature = 'P' | ('K' << 8) | (5 << 16) | (5 << 24); - - /// - /// Central header digital signature - /// - [Obsolete("Use CentralHeaderDigitalSignaure instead")] - public const int CENDIGITALSIG = 'P' | ('K' << 8) | (5 << 16) | (5 << 24); - - /// - /// End of central directory record signature - /// - public const int EndOfCentralDirectorySignature = 'P' | ('K' << 8) | (5 << 16) | (6 << 24); - - /// - /// End of central directory record signature - /// - [Obsolete("Use EndOfCentralDirectorySignature instead")] - public const int ENDSIG = 'P' | ('K' << 8) | (5 << 16) | (6 << 24); - - #endregion Header Signatures - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipEncryptionMethod.cs b/ICSharpCode.SharpZipLib/Zip/ZipEncryptionMethod.cs deleted file mode 100644 index ed51559cdf6b..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipEncryptionMethod.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// The method of encrypting entries when creating zip archives. - /// - public enum ZipEncryptionMethod - { - /// - /// No encryption will be used. - /// - None, - - /// - /// Encrypt entries with ZipCrypto. - /// - ZipCrypto, - - /// - /// Encrypt entries with AES 128. - /// - AES128, - - /// - /// Encrypt entries with AES 256. - /// - AES256 - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs b/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs deleted file mode 100644 index b0bf15821bd8..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs +++ /dev/null @@ -1,1157 +0,0 @@ -using System; -using System.IO; -using System.Text; - -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// Defines known values for the property. - /// - public enum HostSystemID - { - /// - /// Host system = MSDOS - /// - Msdos = 0, - - /// - /// Host system = Amiga - /// - Amiga = 1, - - /// - /// Host system = Open VMS - /// - OpenVms = 2, - - /// - /// Host system = Unix - /// - Unix = 3, - - /// - /// Host system = VMCms - /// - VMCms = 4, - - /// - /// Host system = Atari ST - /// - AtariST = 5, - - /// - /// Host system = OS2 - /// - OS2 = 6, - - /// - /// Host system = Macintosh - /// - Macintosh = 7, - - /// - /// Host system = ZSystem - /// - ZSystem = 8, - - /// - /// Host system = Cpm - /// - Cpm = 9, - - /// - /// Host system = Windows NT - /// - WindowsNT = 10, - - /// - /// Host system = MVS - /// - MVS = 11, - - /// - /// Host system = VSE - /// - Vse = 12, - - /// - /// Host system = Acorn RISC - /// - AcornRisc = 13, - - /// - /// Host system = VFAT - /// - Vfat = 14, - - /// - /// Host system = Alternate MVS - /// - AlternateMvs = 15, - - /// - /// Host system = BEOS - /// - BeOS = 16, - - /// - /// Host system = Tandem - /// - Tandem = 17, - - /// - /// Host system = OS400 - /// - OS400 = 18, - - /// - /// Host system = OSX - /// - OSX = 19, - - /// - /// Host system = WinZIP AES - /// - WinZipAES = 99, - } - - /// - /// This class represents an entry in a zip archive. This can be a file - /// or a directory - /// ZipFile and ZipInputStream will give you instances of this class as - /// information about the members in an archive. ZipOutputStream - /// uses an instance of this class when creating an entry in a Zip file. - ///
- ///
Author of the original java version : Jochen Hoenicke - ///
- public class ZipEntry - { - [Flags] - private enum Known : byte - { - None = 0, - Size = 0x01, - CompressedSize = 0x02, - Crc = 0x04, - Time = 0x08, - ExternalAttributes = 0x10, - } - - #region Constructors - - /// - /// Creates a zip entry with the given name. - /// - /// - /// The name for this entry. Can include directory components. - /// The convention for names is 'unix' style paths with relative names only. - /// There are with no device names and path elements are separated by '/' characters. - /// - /// - /// The name passed is null - /// - public ZipEntry(string name) - : this(name, 0, ZipConstants.VersionMadeBy, CompressionMethod.Deflated, true) - { - } - - /// - /// Creates a zip entry with the given name and version required to extract - /// - /// - /// The name for this entry. Can include directory components. - /// The convention for names is 'unix' style paths with no device names and - /// path elements separated by '/' characters. This is not enforced see CleanName - /// on how to ensure names are valid if this is desired. - /// - /// - /// The minimum 'feature version' required this entry - /// - /// - /// The name passed is null - /// - internal ZipEntry(string name, int versionRequiredToExtract) - : this(name, versionRequiredToExtract, ZipConstants.VersionMadeBy, - CompressionMethod.Deflated, true) - { - } - - /// - /// Initializes an entry with the given name and made by information - /// - /// Name for this entry - /// Version and HostSystem Information - /// Minimum required zip feature version required to extract this entry - /// Compression method for this entry. - /// Whether the entry uses unicode for name and comment - /// - /// The name passed is null - /// - /// - /// versionRequiredToExtract should be 0 (auto-calculate) or > 10 - /// - /// - /// This constructor is used by the ZipFile class when reading from the central header - /// It is not generally useful, use the constructor specifying the name only. - /// - internal ZipEntry(string name, int versionRequiredToExtract, int madeByInfo, - CompressionMethod method, bool unicode) - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - if (name.Length > 0xffff) - { - throw new ArgumentException("Name is too long", nameof(name)); - } - - if ((versionRequiredToExtract != 0) && (versionRequiredToExtract < 10)) - { - throw new ArgumentOutOfRangeException(nameof(versionRequiredToExtract)); - } - - this.DateTime = DateTime.Now; - this.name = name; - this.versionMadeBy = (ushort)madeByInfo; - this.versionToExtract = (ushort)versionRequiredToExtract; - this.method = method; - - IsUnicodeText = unicode; - } - - /// - /// Creates a deep copy of the given zip entry. - /// - /// - /// The entry to copy. - /// - [Obsolete("Use Clone instead")] - public ZipEntry(ZipEntry entry) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - known = entry.known; - name = entry.name; - size = entry.size; - compressedSize = entry.compressedSize; - crc = entry.crc; - dateTime = entry.DateTime; - method = entry.method; - comment = entry.comment; - versionToExtract = entry.versionToExtract; - versionMadeBy = entry.versionMadeBy; - externalFileAttributes = entry.externalFileAttributes; - flags = entry.flags; - - zipFileIndex = entry.zipFileIndex; - offset = entry.offset; - - forceZip64_ = entry.forceZip64_; - - if (entry.extra != null) - { - extra = new byte[entry.extra.Length]; - Array.Copy(entry.extra, 0, extra, 0, entry.extra.Length); - } - } - - #endregion Constructors - - /// - /// Get a value indicating whether the entry has a CRC value available. - /// - public bool HasCrc => (known & Known.Crc) != 0; - - /// - /// Get/Set flag indicating if entry is encrypted. - /// A simple helper routine to aid interpretation of flags - /// - /// This is an assistant that interprets the flags property. - public bool IsCrypted - { - get => this.HasFlag(GeneralBitFlags.Encrypted); - set => this.SetFlag(GeneralBitFlags.Encrypted, value); - } - - /// - /// Get / set a flag indicating whether entry name and comment text are - /// encoded in unicode UTF8. - /// - /// This is an assistant that interprets the flags property. - public bool IsUnicodeText - { - get => this.HasFlag(GeneralBitFlags.UnicodeText); - set => this.SetFlag(GeneralBitFlags.UnicodeText, value); - } - - /// - /// Value used during password checking for PKZIP 2.0 / 'classic' encryption. - /// - internal byte CryptoCheckValue - { - get => cryptoCheckValue_; - set => cryptoCheckValue_ = value; - } - - /// - /// Get/Set general purpose bit flag for entry - /// - /// - /// General purpose bit flag
- ///
- /// Bit 0: If set, indicates the file is encrypted
- /// Bit 1-2 Only used for compression type 6 Imploding, and 8, 9 deflating
- /// Imploding:
- /// Bit 1 if set indicates an 8K sliding dictionary was used. If clear a 4k dictionary was used
- /// Bit 2 if set indicates 3 Shannon-Fanno trees were used to encode the sliding dictionary, 2 otherwise
- ///
- /// Deflating:
- /// Bit 2 Bit 1
- /// 0 0 Normal compression was used
- /// 0 1 Maximum compression was used
- /// 1 0 Fast compression was used
- /// 1 1 Super fast compression was used
- ///
- /// Bit 3: If set, the fields crc-32, compressed size - /// and uncompressed size are were not able to be written during zip file creation - /// The correct values are held in a data descriptor immediately following the compressed data.
- /// Bit 4: Reserved for use by PKZIP for enhanced deflating
- /// Bit 5: If set indicates the file contains compressed patch data
- /// Bit 6: If set indicates strong encryption was used.
- /// Bit 7-10: Unused or reserved
- /// Bit 11: If set the name and comments for this entry are in unicode.
- /// Bit 12-15: Unused or reserved
- ///
- /// - /// - public int Flags - { - get => flags; - set => flags = value; - } - - /// - /// Get/Set index of this entry in Zip file - /// - /// This is only valid when the entry is part of a - public long ZipFileIndex - { - get => zipFileIndex; - set => zipFileIndex = value; - } - - /// - /// Get/set offset for use in central header - /// - public long Offset - { - get => offset; - set => offset = value; - } - - /// - /// Get/Set external file attributes as an integer. - /// The values of this are operating system dependent see - /// HostSystem for details - /// - public int ExternalFileAttributes - { - get => (known & Known.ExternalAttributes) == 0 ? -1 : externalFileAttributes; - - set - { - externalFileAttributes = value; - known |= Known.ExternalAttributes; - } - } - - /// - /// Get the version made by for this entry or zero if unknown. - /// The value / 10 indicates the major version number, and - /// the value mod 10 is the minor version number - /// - public int VersionMadeBy => versionMadeBy & 0xff; - - /// - /// Get a value indicating this entry is for a DOS/Windows system. - /// - public bool IsDOSEntry - => (HostSystem == (int)HostSystemID.Msdos) - || (HostSystem == (int)HostSystemID.WindowsNT); - - /// - /// Test the external attributes for this to - /// see if the external attributes are Dos based (including WINNT and variants) - /// and match the values - /// - /// The attributes to test. - /// Returns true if the external attributes are known to be DOS/Windows - /// based and have the same attributes set as the value passed. - private bool HasDosAttributes(int attributes) - { - bool result = false; - if ((known & Known.ExternalAttributes) != 0) - { - result |= (((HostSystem == (int)HostSystemID.Msdos) || - (HostSystem == (int)HostSystemID.WindowsNT)) && - (ExternalFileAttributes & attributes) == attributes); - } - return result; - } - - /// - /// Gets the compatibility information for the external file attribute - /// 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. Otherwise the value - /// will be non-zero and identify the host system on which the attributes are compatible. - /// - /// - /// - /// The values for this as defined in the Zip File format and by others are shown below. The values are somewhat - /// misleading in some cases as they are not all used as shown. You should consult the relevant documentation - /// to obtain up to date and correct information. The modified appnote by the infozip group is - /// particularly helpful as it documents a lot of peculiarities. The document is however a little dated. - /// - /// 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 HPFS - /// 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) - /// 99 - WinZip AES - /// remainder - unused - /// - /// - public int HostSystem - { - get => (versionMadeBy >> 8) & 0xff; - - set - { - versionMadeBy &= 0x00ff; - versionMadeBy |= (ushort)((value & 0xff) << 8); - } - } - - /// - /// Get minimum Zip feature version required to extract this entry - /// - /// - /// Minimum features are defined as:
- /// 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 encryption
- /// 2.1 - File is compressed using Deflate64
- /// 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.1 - File is encrypted using corrected RC2-64 encryption
- /// 6.1 - File is encrypted using non-OAEP key wrapping
- /// 6.2 - Central directory encryption (not confirmed yet)
- /// 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
- ///
- /// - public int Version - { - get - { - // Return recorded version if known. - if (versionToExtract != 0) - // Only lower order byte. High order is O/S file system. - return versionToExtract & 0x00ff; - - if (AESKeySize > 0) - // Ver 5.1 = AES - return ZipConstants.VERSION_AES; - - if (CompressionMethod.BZip2 == method) - return ZipConstants.VersionBZip2; - - if (CentralHeaderRequiresZip64) - return ZipConstants.VersionZip64; - - if (CompressionMethod.Deflated == method || IsDirectory || IsCrypted) - return 20; - - if (HasDosAttributes(0x08)) - return 11; - - return 10; - } - } - - /// - /// Get a value indicating whether this entry can be decompressed by the library. - /// - /// This is based on the and - /// whether the compression method is supported. - public bool CanDecompress - => Version <= ZipConstants.VersionMadeBy - && (Version == 10 || Version == 11 || Version == 20 || Version == 45 || Version == 46 || Version == 51) - && IsCompressionMethodSupported(); - - /// - /// Force this entry to be recorded using Zip64 extensions. - /// - public void ForceZip64() => forceZip64_ = true; - - /// - /// Get a value indicating whether Zip64 extensions were forced. - /// - /// A value of true if Zip64 extensions have been forced on; false if not. - public bool IsZip64Forced() => forceZip64_; - - /// - /// Gets a value indicating if the entry requires Zip64 extensions - /// to store the full entry values. - /// - /// A value of true if a local header requires Zip64 extensions; false if not. - public bool LocalHeaderRequiresZip64 - { - get - { - bool result = forceZip64_; - - if (!result) - { - ulong trueCompressedSize = compressedSize; - - if ((versionToExtract == 0) && IsCrypted) - { - trueCompressedSize += (ulong)this.EncryptionOverheadSize; - } - - // TODO: A better estimation of the true limit based on compression overhead should be used - // to determine when an entry should use Zip64. - result = - ((this.size >= uint.MaxValue) || (trueCompressedSize >= uint.MaxValue)) && - ((versionToExtract == 0) || (versionToExtract >= ZipConstants.VersionZip64)); - } - - return result; - } - } - - /// - /// Get a value indicating whether the central directory entry requires Zip64 extensions to be stored. - /// - public bool CentralHeaderRequiresZip64 - => LocalHeaderRequiresZip64 || (offset >= uint.MaxValue); - - /// - /// Get/Set DosTime value. - /// - /// - /// The MS-DOS date format can only represent dates between 1/1/1980 and 12/31/2107. - /// - public long DosTime - { - get - { - if ((known & Known.Time) == 0) - { - return 0; - } - - var year = (uint)DateTime.Year; - var month = (uint)DateTime.Month; - var day = (uint)DateTime.Day; - var hour = (uint)DateTime.Hour; - var minute = (uint)DateTime.Minute; - var second = (uint)DateTime.Second; - - if (year < 1980) - { - year = 1980; - month = 1; - day = 1; - hour = 0; - minute = 0; - second = 0; - } - else if (year > 2107) - { - year = 2107; - month = 12; - day = 31; - hour = 23; - minute = 59; - second = 59; - } - - return ((year - 1980) & 0x7f) << 25 | - (month << 21) | - (day << 16) | - (hour << 11) | - (minute << 5) | - (second >> 1); - } - - set - { - unchecked - { - var dosTime = (uint)value; - uint sec = Math.Min(59, 2 * (dosTime & 0x1f)); - uint min = Math.Min(59, (dosTime >> 5) & 0x3f); - uint hrs = Math.Min(23, (dosTime >> 11) & 0x1f); - uint mon = Math.Max(1, Math.Min(12, ((uint)(value >> 21) & 0xf))); - uint year = ((dosTime >> 25) & 0x7f) + 1980; - int day = Math.Max(1, Math.Min(DateTime.DaysInMonth((int)year, (int)mon), (int)((value >> 16) & 0x1f))); - DateTime = new DateTime((int)year, (int)mon, day, (int)hrs, (int)min, (int)sec, DateTimeKind.Unspecified); - } - } - } - - /// - /// Gets/Sets the time of last modification of the entry. - /// - /// - /// The property is updated to match this as far as possible. - /// - public DateTime DateTime - { - get => dateTime; - - set - { - dateTime = value; - known |= Known.Time; - } - } - - /// - /// Returns the entry name. - /// - /// - /// The unix naming convention is followed. - /// Path components in the entry should always separated by forward slashes ('/'). - /// Dos device names like C: should also be removed. - /// See the class, or - /// - public string Name - { - get => name; - internal set => name = value; - } - - /// - /// Gets/Sets the size of the uncompressed data. - /// - /// - /// The size or -1 if unknown. - /// - /// Setting the size before adding an entry to an archive can help - /// avoid compatibility problems with some archivers which don't understand Zip64 extensions. - public long Size - { - get => (known & Known.Size) != 0 ? (long)size : -1L; - set - { - size = (ulong)value; - known |= Known.Size; - } - } - - /// - /// Gets/Sets the size of the compressed data. - /// - /// - /// The compressed entry size or -1 if unknown. - /// - public long CompressedSize - { - get => (known & Known.CompressedSize) != 0 ? (long)compressedSize : -1L; - set - { - compressedSize = (ulong)value; - known |= Known.CompressedSize; - } - } - - /// - /// Gets/Sets the crc of the uncompressed data. - /// - /// - /// Crc is not in the range 0..0xffffffffL - /// - /// - /// The crc value or -1 if unknown. - /// - public long Crc - { - get => (known & Known.Crc) != 0 ? crc & 0xffffffffL : -1L; - set - { - if ((crc & 0xffffffff00000000L) != 0) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - this.crc = (uint)value; - this.known |= Known.Crc; - } - } - - /// - /// Gets/Sets the compression method. - /// - /// - /// The compression method for this entry - /// - public CompressionMethod CompressionMethod - { - get => method; - set => method = value; - } - - /// - /// Gets the compression method for outputting to the local or central header. - /// Returns same value as CompressionMethod except when AES encrypting, which - /// places 99 in the method and places the real method in the extra data. - /// - internal CompressionMethod CompressionMethodForHeader - => (AESKeySize > 0) ? CompressionMethod.WinZipAES : method; - - /// - /// Gets/Sets the extra data. - /// - /// - /// Extra data is longer than 64KB (0xffff) bytes. - /// - /// - /// Extra data or null if not set. - /// - public byte[] ExtraData - { - // TODO: This is slightly safer but less efficient. Think about whether it should change. - // return (byte[]) extra.Clone(); - get => extra; - - set - { - if (value == null) - { - extra = null; - } - else - { - if (value.Length > 0xffff) - { - throw new System.ArgumentOutOfRangeException(nameof(value)); - } - - extra = new byte[value.Length]; - Array.Copy(value, 0, extra, 0, value.Length); - } - } - } - - /// - /// For AES encrypted files returns or sets the number of bits of encryption (128, 192 or 256). - /// When setting, only 0 (off), 128 or 256 is supported. - /// - public int AESKeySize - { - get - { - // the strength (1 or 3) is in the entry header - switch (_aesEncryptionStrength) - { - case 0: - return 0; // Not AES - case 1: - return 128; - - case 2: - return 192; // Not used by WinZip - case 3: - return 256; - - default: - throw new ZipException("Invalid AESEncryptionStrength " + _aesEncryptionStrength); - } - } - set - { - switch (value) - { - case 0: - _aesEncryptionStrength = 0; - break; - - case 128: - _aesEncryptionStrength = 1; - break; - - case 256: - _aesEncryptionStrength = 3; - break; - - default: - throw new ZipException("AESKeySize must be 0, 128 or 256: " + value); - } - } - } - - /// - /// AES Encryption strength for storage in extra data in entry header. - /// 1 is 128 bit, 2 is 192 bit, 3 is 256 bit. - /// - internal byte AESEncryptionStrength => (byte)_aesEncryptionStrength; - - /// - /// Returns the length of the salt, in bytes - /// - /// Key size -> Salt length: 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes. - internal int AESSaltLen => AESKeySize / 16; - - /// - /// Number of extra bytes required to hold the AES Header fields (Salt, Pwd verify, AuthCode) - /// - /// File format: - /// Bytes | Content - /// ---------+--------------------------- - /// Variable | Salt value - /// 2 | Password verification value - /// Variable | Encrypted file data - /// 10 | Authentication code - internal int AESOverheadSize => 12 + AESSaltLen; - - /// - /// Number of extra bytes required to hold the encryption header fields. - /// - internal int EncryptionOverheadSize => - !IsCrypted - // Entry is not encrypted - no overhead - ? 0 - : _aesEncryptionStrength == 0 - // Entry is encrypted using ZipCrypto - ? ZipConstants.CryptoHeaderSize - // Entry is encrypted using AES - : AESOverheadSize; - - /// - /// Process extra data fields updating the entry based on the contents. - /// - /// True if the extra data fields should be handled - /// for a local header, rather than for a central header. - /// - internal void ProcessExtraData(bool localHeader) - { - var extraData = new ZipExtraData(this.extra); - - if (extraData.Find(0x0001)) - { - // Version required to extract is ignored here as some archivers dont set it correctly - // in theory it should be version 45 or higher - - // The recorded size will change but remember that this is zip64. - forceZip64_ = true; - - if (extraData.ValueLength < 4) - { - throw new ZipException("Extra data extended Zip64 information length is invalid"); - } - - // (localHeader ||) was deleted, because actually there is no specific difference with reading sizes between local header & central directory - // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT - // ... - // 4.4 Explanation of fields - // ... - // 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. - // - // Otherwise there is problem with minizip implementation - if (size == uint.MaxValue) - { - size = (ulong)extraData.ReadLong(); - } - - if (compressedSize == uint.MaxValue) - { - compressedSize = (ulong)extraData.ReadLong(); - } - - if (!localHeader && (offset == uint.MaxValue)) - { - offset = extraData.ReadLong(); - } - - // Disk number on which file starts is ignored - } - else - { - if ( - ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) && - ((size == uint.MaxValue) || (compressedSize == uint.MaxValue)) - ) - { - throw new ZipException("Zip64 Extended information required but is missing."); - } - } - - DateTime = GetDateTime(extraData) ?? DateTime; - if (method == CompressionMethod.WinZipAES) - { - ProcessAESExtraData(extraData); - } - } - - private static DateTime? GetDateTime(ZipExtraData extraData) - { - // Check for NT timestamp - // NOTE: Disable by default to match behavior of InfoZIP -#if RESPECT_NT_TIMESTAMP - NTTaggedData ntData = extraData.GetData(); - if (ntData != null) - return ntData.LastModificationTime; -#endif - - // Check for Unix timestamp - ExtendedUnixData unixData = extraData.GetData(); - if (unixData != null && unixData.Include.HasFlag(ExtendedUnixData.Flags.ModificationTime)) - return unixData.ModificationTime; - - return null; - } - - // For AES the method in the entry is 99, and the real compression method is in the extradata - private void ProcessAESExtraData(ZipExtraData extraData) - { - if (extraData.Find(0x9901)) - { - // Set version for Zipfile.CreateAndInitDecryptionStream - versionToExtract = ZipConstants.VERSION_AES; // Ver 5.1 = AES see "Version" getter - - // - // Unpack AES extra data field see http://www.winzip.com/aes_info.htm - int length = extraData.ValueLength; // Data size currently 7 - if (length < 7) - throw new ZipException("AES Extra Data Length " + length + " invalid."); - int ver = extraData.ReadShort(); // Version number (1=AE-1 2=AE-2) - int vendorId = extraData.ReadShort(); // 2-character vendor ID 0x4541 = "AE" - int encrStrength = extraData.ReadByte(); // encryption strength 1 = 128 2 = 192 3 = 256 - int actualCompress = extraData.ReadShort(); // The actual compression method used to compress the file - _aesVer = ver; - _aesEncryptionStrength = encrStrength; - method = (CompressionMethod)actualCompress; - } - else - throw new ZipException("AES Extra Data missing"); - } - - /// - /// Gets/Sets the entry comment. - /// - /// - /// If comment is longer than 0xffff. - /// - /// - /// The comment or null if not set. - /// - /// - /// A comment is only available for entries when read via the class. - /// The class doesn't have the comment data available. - /// - public string Comment - { - get => comment; - set - { - // This test is strictly incorrect as the length is in characters - // while the storage limit is in bytes. - // While the test is partially correct in that a comment of this length or greater - // is definitely invalid, shorter comments may also have an invalid length - // where there are multi-byte characters - // The full test is not possible here however as the code page to apply conversions with - // isn't available. - if ((value != null) && (value.Length > 0xffff)) - { - throw new ArgumentOutOfRangeException(nameof(value), "cannot exceed 65535"); - } - - comment = value; - } - } - - /// - /// Gets a value indicating if the entry is a directory. - /// however. - /// - /// - /// A directory is determined by an entry name with a trailing slash '/'. - /// The external file attributes can also indicate an entry is for a directory. - /// Currently only dos/windows attributes are tested in this manner. - /// The trailing slash convention should always be followed. - /// - public bool IsDirectory - => name.Length > 0 - && (name[name.Length - 1] == '/' || name[name.Length - 1] == '\\') || HasDosAttributes(16); - - /// - /// Get a value of true if the entry appears to be a file; false otherwise - /// - /// - /// This only takes account of DOS/Windows attributes. Other operating systems are ignored. - /// For linux and others the result may be incorrect. - /// - public bool IsFile => !IsDirectory && !HasDosAttributes(8); - - /// - /// Test entry to see if data can be extracted. - /// - /// Returns true if data can be extracted for this entry; false otherwise. - public bool IsCompressionMethodSupported() => IsCompressionMethodSupported(CompressionMethod); - - #region ICloneable Members - - /// - /// Creates a copy of this zip entry. - /// - /// An that is a copy of the current instance. - public object Clone() - { - var result = (ZipEntry)this.MemberwiseClone(); - - // Ensure extra data is unique if it exists. - if (extra != null) - { - result.extra = new byte[extra.Length]; - Array.Copy(extra, 0, result.extra, 0, extra.Length); - } - - return result; - } - - #endregion ICloneable Members - - /// - /// Gets a string representation of this ZipEntry. - /// - /// A readable textual representation of this - public override string ToString() => name; - - /// - /// Test a compression method to see if this library - /// supports extracting data compressed with that method - /// - /// The compression method to test. - /// Returns true if the compression method is supported; false otherwise - public static bool IsCompressionMethodSupported(CompressionMethod method) - => method == CompressionMethod.Deflated - || method == CompressionMethod.Stored - || method == CompressionMethod.BZip2; - - /// - /// Cleans a name making it conform to Zip file conventions. - /// Devices names ('c:\') and UNC share names ('\\server\share') are removed - /// and back slashes ('\') are converted to forward slashes ('/'). - /// Names are made relative by trimming leading slashes which is compatible - /// with the ZIP naming convention. - /// - /// The name to clean - /// The 'cleaned' name. - /// - /// The Zip name transform class is more flexible. - /// - public static string CleanName(string name) - { - if (name == null) - { - return string.Empty; - } - - if (Path.IsPathRooted(name)) - { - // NOTE: - // for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt - name = name.Substring(Path.GetPathRoot(name).Length); - } - - name = name.Replace(@"\", "/"); - - while ((name.Length > 0) && (name[0] == '/')) - { - name = name.Remove(0, 1); - } - return name; - } - - #region Instance Fields - - private Known known; - private int externalFileAttributes = -1; // contains external attributes (O/S dependant) - - private ushort versionMadeBy; // Contains host system and version information - // only relevant for central header entries - - private string name; - private ulong size; - private ulong compressedSize; - private ushort versionToExtract; // Version required to extract (library handles <= 2.0) - private uint crc; - private DateTime dateTime; - - private CompressionMethod method = CompressionMethod.Deflated; - private byte[] extra; - private string comment; - - private int flags; // general purpose bit flags - - private long zipFileIndex = -1; // used by ZipFile - private long offset; // used by ZipFile and ZipOutputStream - - private bool forceZip64_; - private byte cryptoCheckValue_; - private int _aesVer; // Version number (2 = AE-2 ?). Assigned but not used. - private int _aesEncryptionStrength; // Encryption strength 1 = 128 2 = 192 3 = 256 - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipEntryExtensions.cs b/ICSharpCode.SharpZipLib/Zip/ZipEntryExtensions.cs deleted file mode 100644 index 927e94cfe539..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipEntryExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// General ZipEntry helper extensions - /// - public static class ZipEntryExtensions - { - /// - /// Efficiently check if a flag is set without enum un-/boxing - /// - /// - /// - /// Returns whether the flag was set - public static bool HasFlag(this ZipEntry entry, GeneralBitFlags flag) - => (entry.Flags & (int) flag) != 0; - - /// - /// Efficiently set a flag without enum un-/boxing - /// - /// - /// - /// Whether the passed flag should be set (1) or cleared (0) - public static void SetFlag(this ZipEntry entry, GeneralBitFlags flag, bool enabled = true) - => entry.Flags = enabled - ? entry.Flags | (int) flag - : entry.Flags & ~(int) flag; - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs b/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs deleted file mode 100644 index ccbb26968654..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs +++ /dev/null @@ -1,375 +0,0 @@ -using ICSharpCode.SharpZipLib.Core; -using System; -using System.IO; - -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// Basic implementation of - /// - public class ZipEntryFactory : IEntryFactory - { - #region Enumerations - - /// - /// Defines the possible values to be used for the . - /// - public enum TimeSetting - { - /// - /// Use the recorded LastWriteTime value for the file. - /// - LastWriteTime, - - /// - /// Use the recorded LastWriteTimeUtc value for the file - /// - LastWriteTimeUtc, - - /// - /// Use the recorded CreateTime value for the file. - /// - CreateTime, - - /// - /// Use the recorded CreateTimeUtc value for the file. - /// - CreateTimeUtc, - - /// - /// Use the recorded LastAccessTime value for the file. - /// - LastAccessTime, - - /// - /// Use the recorded LastAccessTimeUtc value for the file. - /// - LastAccessTimeUtc, - - /// - /// Use a fixed value. - /// - /// The actual value used can be - /// specified via the constructor or - /// using the with the setting set - /// to which will use the when this class was constructed. - /// The property can also be used to set this value. - Fixed, - } - - #endregion Enumerations - - #region Constructors - - /// - /// Initialise a new instance of the class. - /// - /// A default , and the LastWriteTime for files is used. - public ZipEntryFactory() - { - nameTransform_ = new ZipNameTransform(); - isUnicodeText_ = true; - } - - /// - /// Initialise a new instance of using the specified - /// - /// The time setting to use when creating Zip entries. - public ZipEntryFactory(TimeSetting timeSetting) : this() - { - timeSetting_ = timeSetting; - } - - /// - /// Initialise a new instance of using the specified - /// - /// The time to set all values to. - public ZipEntryFactory(DateTime time) : this() - { - timeSetting_ = TimeSetting.Fixed; - FixedDateTime = time; - } - - #endregion Constructors - - #region Properties - - /// - /// Get / set the to be used when creating new values. - /// - /// - /// Setting this property to null will cause a default name transform to be used. - /// - public INameTransform NameTransform - { - get { return nameTransform_; } - set - { - if (value == null) - { - nameTransform_ = new ZipNameTransform(); - } - else - { - nameTransform_ = value; - } - } - } - - /// - /// Get / set the in use. - /// - public TimeSetting Setting - { - get { return timeSetting_; } - set { timeSetting_ = value; } - } - - /// - /// Get / set the value to use when is set to - /// - public DateTime FixedDateTime - { - get { return fixedDateTime_; } - set - { - if (value.Year < 1970) - { - throw new ArgumentException("Value is too old to be valid", nameof(value)); - } - fixedDateTime_ = value; - } - } - - /// - /// A bitmask defining the attributes to be retrieved from the actual file. - /// - /// The default is to get all possible attributes from the actual file. - public int GetAttributes - { - get { return getAttributes_; } - set { getAttributes_ = value; } - } - - /// - /// A bitmask defining which attributes are to be set on. - /// - /// By default no attributes are set on. - public int SetAttributes - { - get { return setAttributes_; } - set { setAttributes_ = value; } - } - - /// - /// Get set a value indicating whether unicode text should be set on. - /// - public bool IsUnicodeText - { - get { return isUnicodeText_; } - set { isUnicodeText_ = value; } - } - - #endregion Properties - - #region IEntryFactory Members - - /// - /// Make a new for a file. - /// - /// The name of the file to create a new entry for. - /// Returns a new based on the . - public ZipEntry MakeFileEntry(string fileName) - { - return MakeFileEntry(fileName, null, true); - } - - /// - /// Make a new for a file. - /// - /// The name of the file to create a new entry for. - /// If true entry detail is retrieved from the file system if the file exists. - /// Returns a new based on the . - public ZipEntry MakeFileEntry(string fileName, bool useFileSystem) - { - return MakeFileEntry(fileName, null, useFileSystem); - } - - /// - /// Make a new from a name. - /// - /// The name of the file to create a new entry for. - /// An alternative name to be used for the new entry. Null if not applicable. - /// If true entry detail is retrieved from the file system if the file exists. - /// Returns a new based on the . - public ZipEntry MakeFileEntry(string fileName, string entryName, bool useFileSystem) - { - var result = new ZipEntry(nameTransform_.TransformFile(!string.IsNullOrEmpty(entryName) ? entryName : fileName)); - result.IsUnicodeText = isUnicodeText_; - - int externalAttributes = 0; - bool useAttributes = (setAttributes_ != 0); - - FileInfo fi = null; - if (useFileSystem) - { - fi = new FileInfo(fileName); - } - - if ((fi != null) && fi.Exists) - { - switch (timeSetting_) - { - case TimeSetting.CreateTime: - result.DateTime = fi.CreationTime; - break; - - case TimeSetting.CreateTimeUtc: - result.DateTime = fi.CreationTimeUtc; - break; - - case TimeSetting.LastAccessTime: - result.DateTime = fi.LastAccessTime; - break; - - case TimeSetting.LastAccessTimeUtc: - result.DateTime = fi.LastAccessTimeUtc; - break; - - case TimeSetting.LastWriteTime: - result.DateTime = fi.LastWriteTime; - break; - - case TimeSetting.LastWriteTimeUtc: - result.DateTime = fi.LastWriteTimeUtc; - break; - - case TimeSetting.Fixed: - result.DateTime = fixedDateTime_; - break; - - default: - throw new ZipException("Unhandled time setting in MakeFileEntry"); - } - - result.Size = fi.Length; - - useAttributes = true; - externalAttributes = ((int)fi.Attributes & getAttributes_); - } - else - { - if (timeSetting_ == TimeSetting.Fixed) - { - result.DateTime = fixedDateTime_; - } - } - - if (useAttributes) - { - externalAttributes |= setAttributes_; - result.ExternalFileAttributes = externalAttributes; - } - - return result; - } - - /// - /// Make a new for a directory. - /// - /// The raw untransformed name for the new directory - /// Returns a new representing a directory. - public ZipEntry MakeDirectoryEntry(string directoryName) - { - return MakeDirectoryEntry(directoryName, true); - } - - /// - /// Make a new for a directory. - /// - /// The raw untransformed name for the new directory - /// If true entry detail is retrieved from the file system if the file exists. - /// Returns a new representing a directory. - public ZipEntry MakeDirectoryEntry(string directoryName, bool useFileSystem) - { - var result = new ZipEntry(nameTransform_.TransformDirectory(directoryName)); - result.IsUnicodeText = isUnicodeText_; - result.Size = 0; - - int externalAttributes = 0; - - DirectoryInfo di = null; - - if (useFileSystem) - { - di = new DirectoryInfo(directoryName); - } - - if ((di != null) && di.Exists) - { - switch (timeSetting_) - { - case TimeSetting.CreateTime: - result.DateTime = di.CreationTime; - break; - - case TimeSetting.CreateTimeUtc: - result.DateTime = di.CreationTimeUtc; - break; - - case TimeSetting.LastAccessTime: - result.DateTime = di.LastAccessTime; - break; - - case TimeSetting.LastAccessTimeUtc: - result.DateTime = di.LastAccessTimeUtc; - break; - - case TimeSetting.LastWriteTime: - result.DateTime = di.LastWriteTime; - break; - - case TimeSetting.LastWriteTimeUtc: - result.DateTime = di.LastWriteTimeUtc; - break; - - case TimeSetting.Fixed: - result.DateTime = fixedDateTime_; - break; - - default: - throw new ZipException("Unhandled time setting in MakeDirectoryEntry"); - } - - externalAttributes = ((int)di.Attributes & getAttributes_); - } - else - { - if (timeSetting_ == TimeSetting.Fixed) - { - result.DateTime = fixedDateTime_; - } - } - - // Always set directory attribute on. - externalAttributes |= (setAttributes_ | 16); - result.ExternalFileAttributes = externalAttributes; - - return result; - } - - #endregion IEntryFactory Members - - #region Instance Fields - - private INameTransform nameTransform_; - private DateTime fixedDateTime_ = DateTime.Now; - private TimeSetting timeSetting_ = TimeSetting.LastWriteTime; - private bool isUnicodeText_; - - private int getAttributes_ = -1; - private int setAttributes_; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipException.cs b/ICSharpCode.SharpZipLib/Zip/ZipException.cs deleted file mode 100644 index ef8142b6543c..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipException.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// ZipException represents exceptions specific to Zip classes and code. - /// - [Serializable] - public class ZipException : SharpZipBaseException - { - /// - /// Initialise a new instance of . - /// - public ZipException() - { - } - - /// - /// Initialise a new instance of with its message string. - /// - /// A that describes the error. - public ZipException(string message) - : base(message) - { - } - - /// - /// Initialise a new instance of . - /// - /// A that describes the error. - /// The that caused this exception. - public ZipException(string message, Exception innerException) - : base(message, innerException) - { - } - - /// - /// Initializes a new instance of the ZipException class with serialized data. - /// - /// - /// The System.Runtime.Serialization.SerializationInfo that holds the serialized - /// object data about the exception being thrown. - /// - /// - /// The System.Runtime.Serialization.StreamingContext that contains contextual information - /// about the source or destination. - /// - protected ZipException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs b/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs deleted file mode 100644 index cc2e74490fe0..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs +++ /dev/null @@ -1,974 +0,0 @@ -using System; -using System.IO; -using ICSharpCode.SharpZipLib.Core; - -namespace ICSharpCode.SharpZipLib.Zip -{ - // TODO: Sort out whether tagged data is useful and what a good implementation might look like. - // Its just a sketch of an idea at the moment. - - /// - /// ExtraData tagged value interface. - /// - public interface ITaggedData - { - /// - /// Get the ID for this tagged data value. - /// - ushort TagID { get; } - - /// - /// Set the contents of this instance from the data passed. - /// - /// The data to extract contents from. - /// The offset to begin extracting data from. - /// The number of bytes to extract. - void SetData(byte[] data, int offset, int count); - - /// - /// Get the data representing this instance. - /// - /// Returns the data for this instance. - byte[] GetData(); - } - - /// - /// A raw binary tagged value - /// - public class RawTaggedData : ITaggedData - { - /// - /// Initialise a new instance. - /// - /// The tag ID. - public RawTaggedData(ushort tag) - { - _tag = tag; - } - - #region ITaggedData Members - - /// - /// Get the ID for this tagged data value. - /// - public ushort TagID - { - get { return _tag; } - set { _tag = value; } - } - - /// - /// Set the data from the raw values provided. - /// - /// The raw data to extract values from. - /// The index to start extracting values from. - /// The number of bytes available. - public void SetData(byte[] data, int offset, int count) - { - if (data == null) - { - throw new ArgumentNullException(nameof(data)); - } - - _data = new byte[count]; - Array.Copy(data, offset, _data, 0, count); - } - - /// - /// Get the binary data representing this instance. - /// - /// The raw binary data representing this instance. - public byte[] GetData() - { - return _data; - } - - #endregion ITaggedData Members - - /// - /// Get /set the binary data representing this instance. - /// - /// The raw binary data representing this instance. - public byte[] Data - { - get { return _data; } - set { _data = value; } - } - - #region Instance Fields - - /// - /// The tag ID for this instance. - /// - private ushort _tag; - - private byte[] _data; - - #endregion Instance Fields - } - - /// - /// Class representing extended unix date time values. - /// - public class ExtendedUnixData : ITaggedData - { - /// - /// Flags indicate which values are included in this instance. - /// - [Flags] - public enum Flags : byte - { - /// - /// The modification time is included - /// - ModificationTime = 0x01, - - /// - /// The access time is included - /// - AccessTime = 0x02, - - /// - /// The create time is included. - /// - CreateTime = 0x04, - } - - #region ITaggedData Members - - /// - /// Get the ID - /// - public ushort TagID - { - get { return 0x5455; } - } - - /// - /// Set the data from the raw values provided. - /// - /// The raw data to extract values from. - /// The index to start extracting values from. - /// The number of bytes available. - public void SetData(byte[] data, int index, int count) - { - using (MemoryStream ms = new MemoryStream(data, index, count, false)) - { - // bit 0 if set, modification time is present - // bit 1 if set, access time is present - // bit 2 if set, creation time is present - - _flags = (Flags)ms.ReadByte(); - if (((_flags & Flags.ModificationTime) != 0)) - { - int iTime = ms.ReadLEInt(); - - _modificationTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + - new TimeSpan(0, 0, 0, iTime, 0); - - // Central-header version is truncated after modification time - if (count <= 5) return; - } - - if ((_flags & Flags.AccessTime) != 0) - { - int iTime = ms.ReadLEInt(); - - _lastAccessTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + - new TimeSpan(0, 0, 0, iTime, 0); - } - - if ((_flags & Flags.CreateTime) != 0) - { - int iTime = ms.ReadLEInt(); - - _createTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + - new TimeSpan(0, 0, 0, iTime, 0); - } - } - } - - /// - /// Get the binary data representing this instance. - /// - /// The raw binary data representing this instance. - public byte[] GetData() - { - using (MemoryStream ms = new MemoryStream()) - { - ms.WriteByte((byte)_flags); // Flags - if ((_flags & Flags.ModificationTime) != 0) - { - TimeSpan span = _modificationTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - var seconds = (int)span.TotalSeconds; - ms.WriteLEInt(seconds); - } - if ((_flags & Flags.AccessTime) != 0) - { - TimeSpan span = _lastAccessTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - var seconds = (int)span.TotalSeconds; - ms.WriteLEInt(seconds); - } - if ((_flags & Flags.CreateTime) != 0) - { - TimeSpan span = _createTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - var seconds = (int)span.TotalSeconds; - ms.WriteLEInt(seconds); - } - return ms.ToArray(); - } - } - - #endregion ITaggedData Members - - /// - /// Test a value to see if is valid and can be represented here. - /// - /// The value to test. - /// Returns true if the value is valid and can be represented; false if not. - /// The standard Unix time is a signed integer data type, directly encoding the Unix time number, - /// which is the number of seconds since 1970-01-01. - /// Being 32 bits means the values here cover a range of about 136 years. - /// The minimum representable time is 1901-12-13 20:45:52, - /// and the maximum representable time is 2038-01-19 03:14:07. - /// - public static bool IsValidValue(DateTime value) - { - return ((value >= new DateTime(1901, 12, 13, 20, 45, 52)) || - (value <= new DateTime(2038, 1, 19, 03, 14, 07))); - } - - /// - /// Get /set the Modification Time - /// - /// - /// - public DateTime ModificationTime - { - get { return _modificationTime; } - set - { - if (!IsValidValue(value)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - - _flags |= Flags.ModificationTime; - _modificationTime = value; - } - } - - /// - /// Get / set the Access Time - /// - /// - /// - public DateTime AccessTime - { - get { return _lastAccessTime; } - set - { - if (!IsValidValue(value)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - - _flags |= Flags.AccessTime; - _lastAccessTime = value; - } - } - - /// - /// Get / Set the Create Time - /// - /// - /// - public DateTime CreateTime - { - get { return _createTime; } - set - { - if (!IsValidValue(value)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - - _flags |= Flags.CreateTime; - _createTime = value; - } - } - - /// - /// Get/set the values to include. - /// - public Flags Include - { - get { return _flags; } - set { _flags = value; } - } - - #region Instance Fields - - private Flags _flags; - private DateTime _modificationTime = new DateTime(1970, 1, 1); - private DateTime _lastAccessTime = new DateTime(1970, 1, 1); - private DateTime _createTime = new DateTime(1970, 1, 1); - - #endregion Instance Fields - } - - /// - /// Class handling NT date time values. - /// - public class NTTaggedData : ITaggedData - { - /// - /// Get the ID for this tagged data value. - /// - public ushort TagID - { - get { return 10; } - } - - /// - /// Set the data from the raw values provided. - /// - /// The raw data to extract values from. - /// The index to start extracting values from. - /// The number of bytes available. - public void SetData(byte[] data, int index, int count) - { - using (MemoryStream ms = new MemoryStream(data, index, count, false)) - { - ms.ReadLEInt(); // Reserved - while (ms.Position < ms.Length) - { - int ntfsTag = ms.ReadLEShort(); - int ntfsLength = ms.ReadLEShort(); - if (ntfsTag == 1) - { - if (ntfsLength >= 24) - { - long lastModificationTicks = ms.ReadLELong(); - _lastModificationTime = DateTime.FromFileTimeUtc(lastModificationTicks); - - long lastAccessTicks = ms.ReadLELong(); - _lastAccessTime = DateTime.FromFileTimeUtc(lastAccessTicks); - - long createTimeTicks = ms.ReadLELong(); - _createTime = DateTime.FromFileTimeUtc(createTimeTicks); - } - break; - } - else - { - // An unknown NTFS tag so simply skip it. - ms.Seek(ntfsLength, SeekOrigin.Current); - } - } - } - } - - /// - /// Get the binary data representing this instance. - /// - /// The raw binary data representing this instance. - public byte[] GetData() - { - using (MemoryStream ms = new MemoryStream()) - { - ms.WriteLEInt(0); // Reserved - ms.WriteLEShort(1); // Tag - ms.WriteLEShort(24); // Length = 3 x 8. - ms.WriteLELong(_lastModificationTime.ToFileTimeUtc()); - ms.WriteLELong(_lastAccessTime.ToFileTimeUtc()); - ms.WriteLELong(_createTime.ToFileTimeUtc()); - return ms.ToArray(); - } - } - - /// - /// Test a valuie to see if is valid and can be represented here. - /// - /// The value to test. - /// Returns true if the value is valid and can be represented; false if not. - /// - /// NTFS filetimes are 64-bit unsigned integers, stored in Intel - /// (least significant byte first) byte order. They determine the - /// number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch", - /// which is "01-Jan-1601 00:00:00 UTC". 28 May 60056 is the upper limit - /// - public static bool IsValidValue(DateTime value) - { - bool result = true; - try - { - value.ToFileTimeUtc(); - } - catch - { - result = false; - } - return result; - } - - /// - /// Get/set the last modification time. - /// - public DateTime LastModificationTime - { - get { return _lastModificationTime; } - set - { - if (!IsValidValue(value)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - _lastModificationTime = value; - } - } - - /// - /// Get /set the create time - /// - public DateTime CreateTime - { - get { return _createTime; } - set - { - if (!IsValidValue(value)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - _createTime = value; - } - } - - /// - /// Get /set the last access time. - /// - public DateTime LastAccessTime - { - get { return _lastAccessTime; } - set - { - if (!IsValidValue(value)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - _lastAccessTime = value; - } - } - - #region Instance Fields - - private DateTime _lastAccessTime = DateTime.FromFileTimeUtc(0); - private DateTime _lastModificationTime = DateTime.FromFileTimeUtc(0); - private DateTime _createTime = DateTime.FromFileTimeUtc(0); - - #endregion Instance Fields - } - - /// - /// A factory that creates tagged data instances. - /// - internal interface ITaggedDataFactory - { - /// - /// Get data for a specific tag value. - /// - /// The tag ID to find. - /// The data to search. - /// The offset to begin extracting data from. - /// The number of bytes to extract. - /// The located value found, or null if not found. - ITaggedData Create(short tag, byte[] data, int offset, int count); - } - - /// - /// - /// A class to handle the extra data field for Zip entries - /// - /// - /// Extra data contains 0 or more values each prefixed by a header tag and length. - /// They contain zero or more bytes of actual data. - /// The data is held internally using a copy on write strategy. This is more efficient but - /// means that for extra data created by passing in data can have the values modified by the caller - /// in some circumstances. - /// - sealed public class ZipExtraData : IDisposable - { - #region Constructors - - /// - /// Initialise a default instance. - /// - public ZipExtraData() - { - Clear(); - } - - /// - /// Initialise with known extra data. - /// - /// The extra data. - public ZipExtraData(byte[] data) - { - if (data == null) - { - _data = Empty.Array(); - } - else - { - _data = data; - } - } - - #endregion Constructors - - /// - /// Get the raw extra data value - /// - /// Returns the raw byte[] extra data this instance represents. - public byte[] GetEntryData() - { - if (Length > ushort.MaxValue) - { - throw new ZipException("Data exceeds maximum length"); - } - - return (byte[])_data.Clone(); - } - - /// - /// Clear the stored data. - /// - public void Clear() - { - if ((_data == null) || (_data.Length != 0)) - { - _data = Empty.Array(); - } - } - - /// - /// Gets the current extra data length. - /// - public int Length - { - get { return _data.Length; } - } - - /// - /// Get a read-only for the associated tag. - /// - /// The tag to locate data for. - /// Returns a containing tag data or null if no tag was found. - public Stream GetStreamForTag(int tag) - { - Stream result = null; - if (Find(tag)) - { - result = new MemoryStream(_data, _index, _readValueLength, false); - } - return result; - } - - /// - /// Get the tagged data for a tag. - /// - /// The tag to search for. - /// Returns a tagged value or null if none found. - public T GetData() - where T : class, ITaggedData, new() - { - T result = new T(); - if (Find(result.TagID)) - { - result.SetData(_data, _readValueStart, _readValueLength); - return result; - } - else return null; - } - - /// - /// Get the length of the last value found by - /// - /// This is only valid if has previously returned true. - public int ValueLength - { - get { return _readValueLength; } - } - - /// - /// Get the index for the current read value. - /// - /// This is only valid if has previously returned true. - /// Initially the result will be the index of the first byte of actual data. The value is updated after calls to - /// , and . - public int CurrentReadIndex - { - get { return _index; } - } - - /// - /// Get the number of bytes remaining to be read for the current value; - /// - public int UnreadCount - { - get - { - if ((_readValueStart > _data.Length) || - (_readValueStart < 4)) - { - throw new ZipException("Find must be called before calling a Read method"); - } - - return _readValueStart + _readValueLength - _index; - } - } - - /// - /// Find an extra data value - /// - /// The identifier for the value to find. - /// Returns true if the value was found; false otherwise. - public bool Find(int headerID) - { - _readValueStart = _data.Length; - _readValueLength = 0; - _index = 0; - - int localLength = _readValueStart; - int localTag = headerID - 1; - - // Trailing bytes that cant make up an entry (as there arent enough - // bytes for a tag and length) are ignored! - while ((localTag != headerID) && (_index < _data.Length - 3)) - { - localTag = ReadShortInternal(); - localLength = ReadShortInternal(); - if (localTag != headerID) - { - _index += localLength; - } - } - - bool result = (localTag == headerID) && ((_index + localLength) <= _data.Length); - - if (result) - { - _readValueStart = _index; - _readValueLength = localLength; - } - - return result; - } - - /// - /// Add a new entry to extra data. - /// - /// The value to add. - public void AddEntry(ITaggedData taggedData) - { - if (taggedData == null) - { - throw new ArgumentNullException(nameof(taggedData)); - } - AddEntry(taggedData.TagID, taggedData.GetData()); - } - - /// - /// Add a new entry to extra data - /// - /// The ID for this entry. - /// The data to add. - /// If the ID already exists its contents are replaced. - public void AddEntry(int headerID, byte[] fieldData) - { - if ((headerID > ushort.MaxValue) || (headerID < 0)) - { - throw new ArgumentOutOfRangeException(nameof(headerID)); - } - - int addLength = (fieldData == null) ? 0 : fieldData.Length; - - if (addLength > ushort.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(fieldData), "exceeds maximum length"); - } - - // Test for new length before adjusting data. - int newLength = _data.Length + addLength + 4; - - if (Find(headerID)) - { - newLength -= (ValueLength + 4); - } - - if (newLength > ushort.MaxValue) - { - throw new ZipException("Data exceeds maximum length"); - } - - Delete(headerID); - - byte[] newData = new byte[newLength]; - _data.CopyTo(newData, 0); - int index = _data.Length; - _data = newData; - SetShort(ref index, headerID); - SetShort(ref index, addLength); - if (fieldData != null) - { - fieldData.CopyTo(newData, index); - } - } - - /// - /// Start adding a new entry. - /// - /// Add data using , , , or . - /// The new entry is completed and actually added by calling - /// - public void StartNewEntry() - { - _newEntry = new MemoryStream(); - } - - /// - /// Add entry data added since using the ID passed. - /// - /// The identifier to use for this entry. - public void AddNewEntry(int headerID) - { - byte[] newData = _newEntry.ToArray(); - _newEntry = null; - AddEntry(headerID, newData); - } - - /// - /// Add a byte of data to the pending new entry. - /// - /// The byte to add. - /// - public void AddData(byte data) - { - _newEntry.WriteByte(data); - } - - /// - /// Add data to a pending new entry. - /// - /// The data to add. - /// - public void AddData(byte[] data) - { - if (data == null) - { - throw new ArgumentNullException(nameof(data)); - } - - _newEntry.Write(data, 0, data.Length); - } - - /// - /// Add a short value in little endian order to the pending new entry. - /// - /// The data to add. - /// - public void AddLeShort(int toAdd) - { - unchecked - { - _newEntry.WriteByte((byte)toAdd); - _newEntry.WriteByte((byte)(toAdd >> 8)); - } - } - - /// - /// Add an integer value in little endian order to the pending new entry. - /// - /// The data to add. - /// - public void AddLeInt(int toAdd) - { - unchecked - { - AddLeShort((short)toAdd); - AddLeShort((short)(toAdd >> 16)); - } - } - - /// - /// Add a long value in little endian order to the pending new entry. - /// - /// The data to add. - /// - public void AddLeLong(long toAdd) - { - unchecked - { - AddLeInt((int)(toAdd & 0xffffffff)); - AddLeInt((int)(toAdd >> 32)); - } - } - - /// - /// Delete an extra data field. - /// - /// The identifier of the field to delete. - /// Returns true if the field was found and deleted. - public bool Delete(int headerID) - { - bool result = false; - - if (Find(headerID)) - { - result = true; - int trueStart = _readValueStart - 4; - - byte[] newData = new byte[_data.Length - (ValueLength + 4)]; - Array.Copy(_data, 0, newData, 0, trueStart); - - int trueEnd = trueStart + ValueLength + 4; - Array.Copy(_data, trueEnd, newData, trueStart, _data.Length - trueEnd); - _data = newData; - } - return result; - } - - #region Reading Support - - /// - /// Read a long in little endian form from the last found data value - /// - /// Returns the long value read. - public long ReadLong() - { - ReadCheck(8); - return (ReadInt() & 0xffffffff) | (((long)ReadInt()) << 32); - } - - /// - /// Read an integer in little endian form from the last found data value. - /// - /// Returns the integer read. - public int ReadInt() - { - ReadCheck(4); - - int result = _data[_index] + (_data[_index + 1] << 8) + - (_data[_index + 2] << 16) + (_data[_index + 3] << 24); - _index += 4; - return result; - } - - /// - /// Read a short value in little endian form from the last found data value. - /// - /// Returns the short value read. - public int ReadShort() - { - ReadCheck(2); - int result = _data[_index] + (_data[_index + 1] << 8); - _index += 2; - return result; - } - - /// - /// Read a byte from an extra data - /// - /// The byte value read or -1 if the end of data has been reached. - public int ReadByte() - { - int result = -1; - if ((_index < _data.Length) && (_readValueStart + _readValueLength > _index)) - { - result = _data[_index]; - _index += 1; - } - return result; - } - - /// - /// Skip data during reading. - /// - /// The number of bytes to skip. - public void Skip(int amount) - { - ReadCheck(amount); - _index += amount; - } - - private void ReadCheck(int length) - { - if ((_readValueStart > _data.Length) || - (_readValueStart < 4)) - { - throw new ZipException("Find must be called before calling a Read method"); - } - - if (_index > _readValueStart + _readValueLength - length) - { - throw new ZipException("End of extra data"); - } - - if (_index + length < 4) - { - throw new ZipException("Cannot read before start of tag"); - } - } - - /// - /// Internal form of that reads data at any location. - /// - /// Returns the short value read. - private int ReadShortInternal() - { - if (_index > _data.Length - 2) - { - throw new ZipException("End of extra data"); - } - - int result = _data[_index] + (_data[_index + 1] << 8); - _index += 2; - return result; - } - - private void SetShort(ref int index, int source) - { - _data[index] = (byte)source; - _data[index + 1] = (byte)(source >> 8); - index += 2; - } - - #endregion Reading Support - - #region IDisposable Members - - /// - /// Dispose of this instance. - /// - public void Dispose() - { - if (_newEntry != null) - { - _newEntry.Dispose(); - } - } - - #endregion IDisposable Members - - #region Instance Fields - - private int _index; - private int _readValueStart; - private int _readValueLength; - - private MemoryStream _newEntry; - private byte[] _data; - - #endregion Instance Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/ICSharpCode.SharpZipLib/Zip/ZipFile.cs deleted file mode 100644 index ea1fa638ec7e..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipFile.cs +++ /dev/null @@ -1,5059 +0,0 @@ -using ICSharpCode.SharpZipLib.Checksum; -using ICSharpCode.SharpZipLib.Core; -using ICSharpCode.SharpZipLib.Encryption; -using ICSharpCode.SharpZipLib.Zip.Compression; -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace ICSharpCode.SharpZipLib.Zip -{ - #region Keys Required Event Args - - /// - /// Arguments used with KeysRequiredEvent - /// - public class KeysRequiredEventArgs : EventArgs - { - #region Constructors - - /// - /// Initialise a new instance of - /// - /// The name of the file for which keys are required. - public KeysRequiredEventArgs(string name) - { - fileName = name; - } - - /// - /// Initialise a new instance of - /// - /// The name of the file for which keys are required. - /// The current key value. - public KeysRequiredEventArgs(string name, byte[] keyValue) - { - fileName = name; - key = keyValue; - } - - #endregion Constructors - - #region Properties - - /// - /// Gets the name of the file for which keys are required. - /// - public string FileName - { - get { return fileName; } - } - - /// - /// Gets or sets the key value - /// - public byte[] Key - { - get { return key; } - set { key = value; } - } - - #endregion Properties - - #region Instance Fields - - private readonly string fileName; - private byte[] key; - - #endregion Instance Fields - } - - #endregion Keys Required Event Args - - #region Test Definitions - - /// - /// The strategy to apply to testing. - /// - public enum TestStrategy - { - /// - /// Find the first error only. - /// - FindFirstError, - - /// - /// Find all possible errors. - /// - FindAllErrors, - } - - /// - /// The operation in progress reported by a during testing. - /// - /// TestArchive - public enum TestOperation - { - /// - /// Setting up testing. - /// - Initialising, - - /// - /// Testing an individual entries header - /// - EntryHeader, - - /// - /// Testing an individual entries data - /// - EntryData, - - /// - /// Testing an individual entry has completed. - /// - EntryComplete, - - /// - /// Running miscellaneous tests - /// - MiscellaneousTests, - - /// - /// Testing is complete - /// - Complete, - } - - /// - /// Status returned by during testing. - /// - /// TestArchive - public class TestStatus - { - #region Constructors - - /// - /// Initialise a new instance of - /// - /// The this status applies to. - public TestStatus(ZipFile file) - { - file_ = file; - } - - #endregion Constructors - - #region Properties - - /// - /// Get the current in progress. - /// - public TestOperation Operation - { - get { return operation_; } - } - - /// - /// Get the this status is applicable to. - /// - public ZipFile File - { - get { return file_; } - } - - /// - /// Get the current/last entry tested. - /// - public ZipEntry Entry - { - get { return entry_; } - } - - /// - /// Get the number of errors detected so far. - /// - public int ErrorCount - { - get { return errorCount_; } - } - - /// - /// Get the number of bytes tested so far for the current entry. - /// - public long BytesTested - { - get { return bytesTested_; } - } - - /// - /// Get a value indicating whether the last entry test was valid. - /// - public bool EntryValid - { - get { return entryValid_; } - } - - #endregion Properties - - #region Internal API - - internal void AddError() - { - errorCount_++; - entryValid_ = false; - } - - internal void SetOperation(TestOperation operation) - { - operation_ = operation; - } - - internal void SetEntry(ZipEntry entry) - { - entry_ = entry; - entryValid_ = true; - bytesTested_ = 0; - } - - internal void SetBytesTested(long value) - { - bytesTested_ = value; - } - - #endregion Internal API - - #region Instance Fields - - private readonly ZipFile file_; - private ZipEntry entry_; - private bool entryValid_; - private int errorCount_; - private long bytesTested_; - private TestOperation operation_; - - #endregion Instance Fields - } - - /// - /// Delegate invoked during testing if supplied indicating current progress and status. - /// - /// If the message is non-null an error has occured. If the message is null - /// the operation as found in status has started. - public delegate void ZipTestResultHandler(TestStatus status, string message); - - #endregion Test Definitions - - #region Update Definitions - - /// - /// The possible ways of applying updates to an archive. - /// - public enum FileUpdateMode - { - /// - /// Perform all updates on temporary files ensuring that the original file is saved. - /// - Safe, - - /// - /// Update the archive directly, which is faster but less safe. - /// - Direct, - } - - #endregion Update Definitions - - #region ZipFile Class - - /// - /// This class represents a Zip archive. You can ask for the contained - /// entries, or get an input stream for a file entry. The entry is - /// automatically decompressed. - /// - /// You can also update the archive adding or deleting entries. - /// - /// This class is thread safe for input: You can open input streams for arbitrary - /// entries in different threads. - ///
- ///
Author of the original java version : Jochen Hoenicke - ///
- /// - /// - /// using System; - /// using System.Text; - /// using System.Collections; - /// using System.IO; - /// - /// using ICSharpCode.SharpZipLib.Zip; - /// - /// class MainClass - /// { - /// static public void Main(string[] args) - /// { - /// using (ZipFile zFile = new ZipFile(args[0])) { - /// Console.WriteLine("Listing of : " + zFile.Name); - /// Console.WriteLine(""); - /// Console.WriteLine("Raw Size Size Date Time Name"); - /// Console.WriteLine("-------- -------- -------- ------ ---------"); - /// foreach (ZipEntry e in zFile) { - /// if ( e.IsFile ) { - /// DateTime d = e.DateTime; - /// Console.WriteLine("{0, -10}{1, -10}{2} {3} {4}", e.Size, e.CompressedSize, - /// d.ToString("dd-MM-yy"), d.ToString("HH:mm"), - /// e.Name); - /// } - /// } - /// } - /// } - /// } - /// - /// - public class ZipFile : IEnumerable, IDisposable - { - #region KeyHandling - - /// - /// Delegate for handling keys/password setting during compression/decompression. - /// - public delegate void KeysRequiredEventHandler( - object sender, - KeysRequiredEventArgs e - ); - - /// - /// Event handler for handling encryption keys. - /// - public KeysRequiredEventHandler KeysRequired; - - /// - /// Handles getting of encryption keys when required. - /// - /// The file for which encryption keys are required. - private void OnKeysRequired(string fileName) - { - if (KeysRequired != null) - { - var krea = new KeysRequiredEventArgs(fileName, key); - KeysRequired(this, krea); - key = krea.Key; - } - } - - /// - /// Get/set the encryption key value. - /// - private byte[] Key - { - get { return key; } - set { key = value; } - } - - /// - /// Password to be used for encrypting/decrypting files. - /// - /// Set to null if no password is required. - public string Password - { - set - { - if (string.IsNullOrEmpty(value)) - { - key = null; - } - else - { - key = PkzipClassic.GenerateKeys(ZipCryptoEncoding.GetBytes(value)); - } - - rawPassword_ = value; - } - } - - /// - /// Get a value indicating whether encryption keys are currently available. - /// - private bool HaveKeys - { - get { return key != null; } - } - - #endregion KeyHandling - - #region Constructors - - /// - /// Opens a Zip file with the given name for reading. - /// - /// The name of the file to open. - /// - /// The argument supplied is null. - /// - /// An i/o error occurs - /// - /// - /// The file doesn't contain a valid zip archive. - /// - public ZipFile(string name, StringCodec stringCodec = null) - { - name_ = name ?? throw new ArgumentNullException(nameof(name)); - - baseStream_ = File.Open(name, FileMode.Open, FileAccess.Read, FileShare.Read); - isStreamOwner = true; - - if (stringCodec != null) - { - _stringCodec = stringCodec; - } - - try - { - ReadEntries(); - } - catch - { - DisposeInternal(true); - throw; - } - } - - /// - /// Opens a Zip file reading the given . - /// - /// The to read archive data from. - /// The supplied argument is null. - /// - /// An i/o error occurs. - /// - /// - /// The file doesn't contain a valid zip archive. - /// - public ZipFile(FileStream file) : - this(file, false) - { - - } - - /// - /// Opens a Zip file reading the given . - /// - /// The to read archive data from. - /// true to leave the file open when the ZipFile is disposed, false to dispose of it - /// The supplied argument is null. - /// - /// An i/o error occurs. - /// - /// - /// The file doesn't contain a valid zip archive. - /// - public ZipFile(FileStream file, bool leaveOpen) - { - if (file == null) - { - throw new ArgumentNullException(nameof(file)); - } - - if (!file.CanSeek) - { - throw new ArgumentException("Stream is not seekable", nameof(file)); - } - - baseStream_ = file; - name_ = file.Name; - isStreamOwner = !leaveOpen; - - try - { - ReadEntries(); - } - catch - { - DisposeInternal(true); - throw; - } - } - - /// - /// Opens a Zip file reading the given . - /// - /// The to read archive data from. - /// - /// An i/o error occurs - /// - /// - /// The stream doesn't contain a valid zip archive.
- ///
- /// - /// The stream doesnt support seeking. - /// - /// - /// The stream argument is null. - /// - public ZipFile(Stream stream) : - this(stream, false) - { - - } - - /// - /// Opens a Zip file reading the given . - /// - /// The to read archive data from. - /// true to leave the stream open when the ZipFile is disposed, false to dispose of it - /// - /// An i/o error occurs - /// - /// - /// The stream doesn't contain a valid zip archive.
- ///
- /// - /// The stream doesnt support seeking. - /// - /// - /// The stream argument is null. - /// - public ZipFile(Stream stream, bool leaveOpen) - { - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!stream.CanSeek) - { - throw new ArgumentException("Stream is not seekable", nameof(stream)); - } - - baseStream_ = stream; - isStreamOwner = !leaveOpen; - - if (baseStream_.Length > 0) - { - try - { - ReadEntries(); - } - catch - { - DisposeInternal(true); - throw; - } - } - else - { - entries_ = Empty.Array(); - isNewArchive_ = true; - } - } - - /// - /// Initialises a default instance with no entries and no file storage. - /// - internal ZipFile() - { - entries_ = Empty.Array(); - isNewArchive_ = true; - } - - #endregion Constructors - - #region Destructors and Closing - - /// - /// Finalize this instance. - /// - ~ZipFile() - { - Dispose(false); - } - - /// - /// Closes the ZipFile. If the stream is owned then this also closes the underlying input stream. - /// Once closed, no further instance methods should be called. - /// - /// - /// An i/o error occurs. - /// - public void Close() - { - DisposeInternal(true); - GC.SuppressFinalize(this); - } - - #endregion Destructors and Closing - - #region Creators - - /// - /// Create a new whose data will be stored in a file. - /// - /// The name of the archive to create. - /// Returns the newly created - /// is null - public static ZipFile Create(string fileName) - { - if (fileName == null) - { - throw new ArgumentNullException(nameof(fileName)); - } - - FileStream fs = File.Create(fileName); - - return new ZipFile - { - name_ = fileName, - baseStream_ = fs, - isStreamOwner = true - }; - } - - /// - /// Create a new whose data will be stored on a stream. - /// - /// The stream providing data storage. - /// Returns the newly created - /// is null - /// doesnt support writing. - public static ZipFile Create(Stream outStream) - { - if (outStream == null) - { - throw new ArgumentNullException(nameof(outStream)); - } - - if (!outStream.CanWrite) - { - throw new ArgumentException("Stream is not writeable", nameof(outStream)); - } - - if (!outStream.CanSeek) - { - throw new ArgumentException("Stream is not seekable", nameof(outStream)); - } - - var result = new ZipFile - { - baseStream_ = outStream - }; - return result; - } - - #endregion Creators - - #region Properties - - /// - /// Get/set a flag indicating if the underlying stream is owned by the ZipFile instance. - /// If the flag is true then the stream will be closed when Close is called. - /// - /// - /// The default value is true in all cases. - /// - public bool IsStreamOwner - { - get { return isStreamOwner; } - set { isStreamOwner = value; } - } - - /// - /// Get a value indicating whether - /// this archive is embedded in another file or not. - /// - public bool IsEmbeddedArchive - { - // Not strictly correct in all circumstances currently - get { return offsetOfFirstEntry > 0; } - } - - /// - /// Get a value indicating that this archive is a new one. - /// - public bool IsNewArchive - { - get { return isNewArchive_; } - } - - /// - /// Gets the comment for the zip file. - /// - public string ZipFileComment - { - get { return comment_; } - } - - /// - /// Gets the name of this zip file. - /// - public string Name - { - get { return name_; } - } - - /// - /// Gets the number of entries in this zip file. - /// - /// - /// The Zip file has been closed. - /// - [Obsolete("Use the Count property instead")] - public int Size - { - get - { - return entries_.Length; - } - } - - /// - /// Get the number of entries contained in this . - /// - public long Count - { - get - { - return entries_.Length; - } - } - - /// - /// Indexer property for ZipEntries - /// - [System.Runtime.CompilerServices.IndexerNameAttribute("EntryByIndex")] - public ZipEntry this[int index] - { - get - { - return (ZipEntry)entries_[index].Clone(); - } - } - - - /// - public Encoding ZipCryptoEncoding - { - get => _stringCodec.ZipCryptoEncoding; - set => _stringCodec.ZipCryptoEncoding = value; - } - - /// - public StringCodec StringCodec - { - get => _stringCodec; - set => _stringCodec = value; - } - - #endregion Properties - - #region Input Handling - - /// - /// Gets an enumerator for the Zip entries in this Zip file. - /// - /// Returns an for this archive. - /// - /// The Zip file has been closed. - /// - public IEnumerator GetEnumerator() - { - if (isDisposed_) - { - throw new ObjectDisposedException("ZipFile"); - } - - return new ZipEntryEnumerator(entries_); - } - - /// - /// Return the index of the entry with a matching name - /// - /// Entry name to find - /// If true the comparison is case insensitive - /// The index position of the matching entry or -1 if not found - /// - /// The Zip file has been closed. - /// - public int FindEntry(string name, bool ignoreCase) - { - if (isDisposed_) - { - throw new ObjectDisposedException("ZipFile"); - } - - // TODO: This will be slow as the next ice age for huge archives! - for (int i = 0; i < entries_.Length; i++) - { - if (string.Compare(name, entries_[i].Name, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0) - { - return i; - } - } - return -1; - } - - /// - /// Searches for a zip entry in this archive with the given name. - /// String comparisons are case insensitive - /// - /// - /// The name to find. May contain directory components separated by slashes ('/'). - /// - /// - /// A clone of the zip entry, or null if no entry with that name exists. - /// - /// - /// The Zip file has been closed. - /// - public ZipEntry GetEntry(string name) - { - if (isDisposed_) - { - throw new ObjectDisposedException("ZipFile"); - } - - int index = FindEntry(name, true); - return (index >= 0) ? (ZipEntry)entries_[index].Clone() : null; - } - - /// - /// Gets an input stream for reading the given zip entry data in an uncompressed form. - /// Normally the should be an entry returned by GetEntry(). - /// - /// The to obtain a data for - /// An input containing data for this - /// - /// The ZipFile has already been closed - /// - /// - /// The compression method for the entry is unknown - /// - /// - /// The entry is not found in the ZipFile - /// - public Stream GetInputStream(ZipEntry entry) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - if (isDisposed_) - { - throw new ObjectDisposedException("ZipFile"); - } - - long index = entry.ZipFileIndex; - if ((index < 0) || (index >= entries_.Length) || (entries_[index].Name != entry.Name)) - { - index = FindEntry(entry.Name, true); - if (index < 0) - { - throw new ZipException("Entry cannot be found"); - } - } - return GetInputStream(index); - } - - /// - /// Creates an input stream reading a zip entry - /// - /// The index of the entry to obtain an input stream for. - /// - /// An input containing data for this - /// - /// - /// The ZipFile has already been closed - /// - /// - /// The compression method for the entry is unknown - /// - /// - /// The entry is not found in the ZipFile - /// - public Stream GetInputStream(long entryIndex) - { - if (isDisposed_) - { - throw new ObjectDisposedException("ZipFile"); - } - - long start = LocateEntry(entries_[entryIndex]); - CompressionMethod method = entries_[entryIndex].CompressionMethod; - Stream result = new PartialInputStream(this, start, entries_[entryIndex].CompressedSize); - - if (entries_[entryIndex].IsCrypted == true) - { - result = CreateAndInitDecryptionStream(result, entries_[entryIndex]); - if (result == null) - { - throw new ZipException("Unable to decrypt this entry"); - } - } - - switch (method) - { - case CompressionMethod.Stored: - // read as is. - break; - - case CompressionMethod.Deflated: - // No need to worry about ownership and closing as underlying stream close does nothing. - result = new InflaterInputStream(result, new Inflater(true)); - break; - - case CompressionMethod.BZip2: - result = new BZip2.BZip2InputStream(result); - break; - - default: - throw new ZipException("Unsupported compression method " + method); - } - - return result; - } - - #endregion Input Handling - - #region Archive Testing - - /// - /// Test an archive for integrity/validity - /// - /// Perform low level data Crc check - /// true if all tests pass, false otherwise - /// Testing will terminate on the first error found. - public bool TestArchive(bool testData) - { - return TestArchive(testData, TestStrategy.FindFirstError, null); - } - - /// - /// Test an archive for integrity/validity - /// - /// Perform low level data Crc check - /// The to apply. - /// The handler to call during testing. - /// true if all tests pass, false otherwise - /// The object has already been closed. - public bool TestArchive(bool testData, TestStrategy strategy, ZipTestResultHandler resultHandler) - { - if (isDisposed_) - { - throw new ObjectDisposedException("ZipFile"); - } - - var status = new TestStatus(this); - - resultHandler?.Invoke(status, null); - - HeaderTest test = testData ? (HeaderTest.Header | HeaderTest.Extract) : HeaderTest.Header; - - bool testing = true; - - try - { - int entryIndex = 0; - - while (testing && (entryIndex < Count)) - { - if (resultHandler != null) - { - status.SetEntry(this[entryIndex]); - status.SetOperation(TestOperation.EntryHeader); - resultHandler(status, null); - } - - try - { - TestLocalHeader(this[entryIndex], test); - } - catch (ZipException ex) - { - status.AddError(); - - resultHandler?.Invoke(status, $"Exception during test - '{ex.Message}'"); - - testing &= strategy != TestStrategy.FindFirstError; - } - - if (testing && testData && this[entryIndex].IsFile) - { - // Don't check CRC for AES encrypted archives - var checkCRC = this[entryIndex].AESKeySize == 0; - - if (resultHandler != null) - { - status.SetOperation(TestOperation.EntryData); - resultHandler(status, null); - } - - var crc = new Crc32(); - - using (Stream entryStream = this.GetInputStream(this[entryIndex])) - { - byte[] buffer = new byte[4096]; - long totalBytes = 0; - int bytesRead; - while ((bytesRead = entryStream.Read(buffer, 0, buffer.Length)) > 0) - { - if (checkCRC) - { - crc.Update(new ArraySegment(buffer, 0, bytesRead)); - } - - if (resultHandler != null) - { - totalBytes += bytesRead; - status.SetBytesTested(totalBytes); - resultHandler(status, null); - } - } - } - - if (checkCRC && this[entryIndex].Crc != crc.Value) - { - status.AddError(); - - resultHandler?.Invoke(status, "CRC mismatch"); - - testing &= strategy != TestStrategy.FindFirstError; - } - - if ((this[entryIndex].Flags & (int)GeneralBitFlags.Descriptor) != 0) - { - var data = new DescriptorData(); - ZipFormat.ReadDataDescriptor(baseStream_, this[entryIndex].LocalHeaderRequiresZip64, data); - if (checkCRC && this[entryIndex].Crc != data.Crc) - { - status.AddError(); - resultHandler?.Invoke(status, "Descriptor CRC mismatch"); - } - - if (this[entryIndex].CompressedSize != data.CompressedSize) - { - status.AddError(); - resultHandler?.Invoke(status, "Descriptor compressed size mismatch"); - } - - if (this[entryIndex].Size != data.Size) - { - status.AddError(); - resultHandler?.Invoke(status, "Descriptor size mismatch"); - } - } - } - - if (resultHandler != null) - { - status.SetOperation(TestOperation.EntryComplete); - resultHandler(status, null); - } - - entryIndex += 1; - } - - if (resultHandler != null) - { - status.SetOperation(TestOperation.MiscellaneousTests); - resultHandler(status, null); - } - - // TODO: the 'Corrina Johns' test where local headers are missing from - // the central directory. They are therefore invisible to many archivers. - } - catch (Exception ex) - { - status.AddError(); - - resultHandler?.Invoke(status, $"Exception during test - '{ex.Message}'"); - } - - if (resultHandler != null) - { - status.SetOperation(TestOperation.Complete); - status.SetEntry(null); - resultHandler(status, null); - } - - return (status.ErrorCount == 0); - } - - [Flags] - private enum HeaderTest - { - Extract = 0x01, // Check that this header represents an entry whose data can be extracted - Header = 0x02, // Check that this header contents are valid - } - - /// - /// Test a local header against that provided from the central directory - /// - /// - /// The entry to test against - /// - /// The type of tests to carry out. - /// The offset of the entries data in the file - private long TestLocalHeader(ZipEntry entry, HeaderTest tests) - { - lock (baseStream_) - { - bool testHeader = (tests & HeaderTest.Header) != 0; - bool testData = (tests & HeaderTest.Extract) != 0; - - var entryAbsOffset = offsetOfFirstEntry + entry.Offset; - - baseStream_.Seek(entryAbsOffset, SeekOrigin.Begin); - var signature = (int)ReadLEUint(); - - if (signature != ZipConstants.LocalHeaderSignature) - { - throw new ZipException(string.Format("Wrong local header signature at 0x{0:x}, expected 0x{1:x8}, actual 0x{2:x8}", - entryAbsOffset, ZipConstants.LocalHeaderSignature, signature)); - } - - var extractVersion = (short)(ReadLEUshort() & 0x00ff); - var localFlags = (short)ReadLEUshort(); - var compressionMethod = (short)ReadLEUshort(); - var fileTime = (short)ReadLEUshort(); - var fileDate = (short)ReadLEUshort(); - uint crcValue = ReadLEUint(); - long compressedSize = ReadLEUint(); - long size = ReadLEUint(); - int storedNameLength = ReadLEUshort(); - int extraDataLength = ReadLEUshort(); - - byte[] nameData = new byte[storedNameLength]; - StreamUtils.ReadFully(baseStream_, nameData); - - byte[] extraData = new byte[extraDataLength]; - StreamUtils.ReadFully(baseStream_, extraData); - - var localExtraData = new ZipExtraData(extraData); - - // Extra data / zip64 checks - if (localExtraData.Find(1)) - { - // 2010-03-04 Forum 10512: removed checks for version >= ZipConstants.VersionZip64 - // and size or compressedSize = MaxValue, due to rogue creators. - - size = localExtraData.ReadLong(); - compressedSize = localExtraData.ReadLong(); - - if ((localFlags & (int)GeneralBitFlags.Descriptor) != 0) - { - // These may be valid if patched later - if ((size != -1) && (size != entry.Size)) - { - throw new ZipException("Size invalid for descriptor"); - } - - if ((compressedSize != -1) && (compressedSize != entry.CompressedSize)) - { - throw new ZipException("Compressed size invalid for descriptor"); - } - } - } - else - { - // No zip64 extra data but entry requires it. - if ((extractVersion >= ZipConstants.VersionZip64) && - (((uint)size == uint.MaxValue) || ((uint)compressedSize == uint.MaxValue))) - { - throw new ZipException("Required Zip64 extended information missing"); - } - } - - if (testData) - { - if (entry.IsFile) - { - if (!entry.IsCompressionMethodSupported()) - { - throw new ZipException("Compression method not supported"); - } - - if ((extractVersion > ZipConstants.VersionMadeBy) - || ((extractVersion > 20) && (extractVersion < ZipConstants.VersionZip64))) - { - throw new ZipException(string.Format("Version required to extract this entry not supported ({0})", extractVersion)); - } - - if ((localFlags & (int)(GeneralBitFlags.Patched | GeneralBitFlags.StrongEncryption | GeneralBitFlags.EnhancedCompress | GeneralBitFlags.HeaderMasked)) != 0) - { - throw new ZipException("The library does not support the zip version required to extract this entry"); - } - } - } - - if (testHeader) - { - if ((extractVersion <= 63) && // Ignore later versions as we dont know about them.. - (extractVersion != 10) && - (extractVersion != 11) && - (extractVersion != 20) && - (extractVersion != 21) && - (extractVersion != 25) && - (extractVersion != 27) && - (extractVersion != 45) && - (extractVersion != 46) && - (extractVersion != 50) && - (extractVersion != 51) && - (extractVersion != 52) && - (extractVersion != 61) && - (extractVersion != 62) && - (extractVersion != 63) - ) - { - throw new ZipException(string.Format("Version required to extract this entry is invalid ({0})", extractVersion)); - } - - var localEncoding = _stringCodec.ZipInputEncoding(localFlags); - - // Local entry flags dont have reserved bit set on. - if ((localFlags & (int)(GeneralBitFlags.ReservedPKware4 | GeneralBitFlags.ReservedPkware14 | GeneralBitFlags.ReservedPkware15)) != 0) - { - throw new ZipException("Reserved bit flags cannot be set."); - } - - // Encryption requires extract version >= 20 - if (((localFlags & (int)GeneralBitFlags.Encrypted) != 0) && (extractVersion < 20)) - { - throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0})", extractVersion)); - } - - // Strong encryption requires encryption flag to be set and extract version >= 50. - if ((localFlags & (int)GeneralBitFlags.StrongEncryption) != 0) - { - if ((localFlags & (int)GeneralBitFlags.Encrypted) == 0) - { - throw new ZipException("Strong encryption flag set but encryption flag is not set"); - } - - if (extractVersion < 50) - { - throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0})", extractVersion)); - } - } - - // Patched entries require extract version >= 27 - if (((localFlags & (int)GeneralBitFlags.Patched) != 0) && (extractVersion < 27)) - { - throw new ZipException(string.Format("Patched data requires higher version than ({0})", extractVersion)); - } - - // Central header flags match local entry flags. - if (localFlags != entry.Flags) - { - throw new ZipException("Central header/local header flags mismatch"); - } - - // Central header compression method matches local entry - if (entry.CompressionMethodForHeader != (CompressionMethod)compressionMethod) - { - throw new ZipException("Central header/local header compression method mismatch"); - } - - if (entry.Version != extractVersion) - { - throw new ZipException("Extract version mismatch"); - } - - // Strong encryption and extract version match - if ((localFlags & (int)GeneralBitFlags.StrongEncryption) != 0) - { - if (extractVersion < 62) - { - throw new ZipException("Strong encryption flag set but version not high enough"); - } - } - - if ((localFlags & (int)GeneralBitFlags.HeaderMasked) != 0) - { - if ((fileTime != 0) || (fileDate != 0)) - { - throw new ZipException("Header masked set but date/time values non-zero"); - } - } - - if ((localFlags & (int)GeneralBitFlags.Descriptor) == 0) - { - if (crcValue != (uint)entry.Crc) - { - throw new ZipException("Central header/local header crc mismatch"); - } - } - - // Crc valid for empty entry. - // This will also apply to streamed entries where size isnt known and the header cant be patched - if ((size == 0) && (compressedSize == 0)) - { - if (crcValue != 0) - { - throw new ZipException("Invalid CRC for empty entry"); - } - } - - // TODO: make test more correct... can't compare lengths as was done originally as this can fail for MBCS strings - // Assuming a code page at this point is not valid? Best is to store the name length in the ZipEntry probably - if (entry.Name.Length > storedNameLength) - { - throw new ZipException("File name length mismatch"); - } - - // Name data has already been read convert it and compare. - string localName = localEncoding.GetString(nameData); - - // Central directory and local entry name match - if (localName != entry.Name) - { - throw new ZipException("Central header and local header file name mismatch"); - } - - // Directories have zero actual size but can have compressed size - if (entry.IsDirectory) - { - if (size > 0) - { - throw new ZipException("Directory cannot have size"); - } - - // There may be other cases where the compressed size can be greater than this? - // If so until details are known we will be strict. - if (entry.IsCrypted) - { - if (compressedSize > entry.EncryptionOverheadSize + 2) - { - throw new ZipException("Directory compressed size invalid"); - } - } - else if (compressedSize > 2) - { - // When not compressed the directory size can validly be 2 bytes - // if the true size wasn't known when data was originally being written. - // NOTE: Versions of the library 0.85.4 and earlier always added 2 bytes - throw new ZipException("Directory compressed size invalid"); - } - } - - if (!ZipNameTransform.IsValidName(localName, true)) - { - throw new ZipException("Name is invalid"); - } - } - - // Tests that apply to both data and header. - - // Size can be verified only if it is known in the local header. - // it will always be known in the central header. - if (((localFlags & (int)GeneralBitFlags.Descriptor) == 0) || - ((size > 0 || compressedSize > 0) && entry.Size > 0)) - { - if ((size != 0) - && (size != entry.Size)) - { - throw new ZipException( - string.Format("Size mismatch between central header({0}) and local header({1})", - entry.Size, size)); - } - - if ((compressedSize != 0) - && (compressedSize != entry.CompressedSize && compressedSize != 0xFFFFFFFF && compressedSize != -1)) - { - throw new ZipException( - string.Format("Compressed size mismatch between central header({0}) and local header({1})", - entry.CompressedSize, compressedSize)); - } - } - - int extraLength = storedNameLength + extraDataLength; - return offsetOfFirstEntry + entry.Offset + ZipConstants.LocalHeaderBaseSize + extraLength; - } - } - - #endregion Archive Testing - - #region Updating - - private const int DefaultBufferSize = 4096; - - /// - /// The kind of update to apply. - /// - private enum UpdateCommand - { - Copy, // Copy original file contents. - Modify, // Change encryption, compression, attributes, name, time etc, of an existing file. - Add, // Add a new file to the archive. - } - - #region Properties - - /// - /// Get / set the to apply to names when updating. - /// - public INameTransform NameTransform - { - get - { - return updateEntryFactory_.NameTransform; - } - - set - { - updateEntryFactory_.NameTransform = value; - } - } - - /// - /// Get/set the used to generate values - /// during updates. - /// - public IEntryFactory EntryFactory - { - get - { - return updateEntryFactory_; - } - - set - { - if (value == null) - { - updateEntryFactory_ = new ZipEntryFactory(); - } - else - { - updateEntryFactory_ = value; - } - } - } - - /// - /// Get /set the buffer size to be used when updating this zip file. - /// - public int BufferSize - { - get { return bufferSize_; } - set - { - if (value < 1024) - { - throw new ArgumentOutOfRangeException(nameof(value), "cannot be below 1024"); - } - - if (bufferSize_ != value) - { - bufferSize_ = value; - copyBuffer_ = null; - } - } - } - - /// - /// Get a value indicating an update has been started. - /// - public bool IsUpdating - { - get { return updates_ != null; } - } - - /// - /// Get / set a value indicating how Zip64 Extension usage is determined when adding entries. - /// - public UseZip64 UseZip64 - { - get { return useZip64_; } - set { useZip64_ = value; } - } - - #endregion Properties - - #region Immediate updating - - // TBD: Direct form of updating - // - // public void Update(IEntryMatcher deleteMatcher) - // { - // } - // - // public void Update(IScanner addScanner) - // { - // } - - #endregion Immediate updating - - #region Deferred Updating - - /// - /// Begin updating this archive. - /// - /// The archive storage for use during the update. - /// The data source to utilise during updating. - /// ZipFile has been closed. - /// One of the arguments provided is null - /// ZipFile has been closed. - public void BeginUpdate(IArchiveStorage archiveStorage, IDynamicDataSource dataSource) - { - if (isDisposed_) - { - throw new ObjectDisposedException("ZipFile"); - } - - if (IsEmbeddedArchive) - { - throw new ZipException("Cannot update embedded/SFX archives"); - } - - archiveStorage_ = archiveStorage ?? throw new ArgumentNullException(nameof(archiveStorage)); - updateDataSource_ = dataSource ?? throw new ArgumentNullException(nameof(dataSource)); - - // NOTE: the baseStream_ may not currently support writing or seeking. - - updateIndex_ = new Dictionary(); - - updates_ = new List(entries_.Length); - foreach (ZipEntry entry in entries_) - { - int index = updates_.Count; - updates_.Add(new ZipUpdate(entry)); - updateIndex_.Add(entry.Name, index); - } - - // We must sort by offset before using offset's calculated sizes - updates_.Sort(new UpdateComparer()); - - int idx = 0; - foreach (ZipUpdate update in updates_) - { - //If last entry, there is no next entry offset to use - if (idx == updates_.Count - 1) - break; - - update.OffsetBasedSize = ((ZipUpdate)updates_[idx + 1]).Entry.Offset - update.Entry.Offset; - idx++; - } - updateCount_ = updates_.Count; - - contentsEdited_ = false; - commentEdited_ = false; - newComment_ = null; - } - - /// - /// Begin updating to this archive. - /// - /// The storage to use during the update. - public void BeginUpdate(IArchiveStorage archiveStorage) - { - BeginUpdate(archiveStorage, new DynamicDiskDataSource()); - } - - /// - /// Begin updating this archive. - /// - /// - /// - /// - public void BeginUpdate() - { - if (Name == null) - { - BeginUpdate(new MemoryArchiveStorage(), new DynamicDiskDataSource()); - } - else - { - BeginUpdate(new DiskArchiveStorage(this), new DynamicDiskDataSource()); - } - } - - /// - /// Commit current updates, updating this archive. - /// - /// - /// - /// ZipFile has been closed. - public void CommitUpdate() - { - if (isDisposed_) - { - throw new ObjectDisposedException("ZipFile"); - } - - CheckUpdating(); - - try - { - updateIndex_.Clear(); - updateIndex_ = null; - - if (contentsEdited_) - { - RunUpdates(); - } - else if (commentEdited_) - { - UpdateCommentOnly(); - } - else - { - // Create an empty archive if none existed originally. - if (entries_.Length != 0) return; - byte[] theComment = (newComment_ != null) - ? newComment_.RawComment - : _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment_); - ZipFormat.WriteEndOfCentralDirectory(baseStream_, 0, 0, 0, theComment); - } - } - finally - { - PostUpdateCleanup(); - } - } - - /// - /// Abort updating leaving the archive unchanged. - /// - /// - /// - public void AbortUpdate() - { - PostUpdateCleanup(); - } - - /// - /// Set the file comment to be recorded when the current update is commited. - /// - /// The comment to record. - /// ZipFile has been closed. - public void SetComment(string comment) - { - if (isDisposed_) - { - throw new ObjectDisposedException("ZipFile"); - } - - CheckUpdating(); - - newComment_ = new ZipString(comment, _stringCodec.ZipArchiveCommentEncoding); - - if (newComment_.RawLength > 0xffff) - { - newComment_ = null; - throw new ZipException("Comment length exceeds maximum - 65535"); - } - - // We dont take account of the original and current comment appearing to be the same - // as encoding may be different. - commentEdited_ = true; - } - - #endregion Deferred Updating - - #region Adding Entries - - private void AddUpdate(ZipUpdate update) - { - contentsEdited_ = true; - - int index = FindExistingUpdate(update.Entry.Name, isEntryName: true); - - if (index >= 0) - { - if (updates_[index] == null) - { - updateCount_ += 1; - } - - // Direct replacement is faster than delete and add. - updates_[index] = update; - } - else - { - index = updates_.Count; - updates_.Add(update); - updateCount_ += 1; - updateIndex_.Add(update.Entry.Name, index); - } - } - - /// - /// Add a new entry to the archive. - /// - /// The name of the file to add. - /// The compression method to use. - /// Ensure Unicode text is used for name and comment for this entry. - /// Argument supplied is null. - /// ZipFile has been closed. - /// Compression method is not supported for creating entries. - public void Add(string fileName, CompressionMethod compressionMethod, bool useUnicodeText) - { - if (fileName == null) - { - throw new ArgumentNullException(nameof(fileName)); - } - - if (isDisposed_) - { - throw new ObjectDisposedException("ZipFile"); - } - - CheckSupportedCompressionMethod(compressionMethod); - CheckUpdating(); - contentsEdited_ = true; - - ZipEntry entry = EntryFactory.MakeFileEntry(fileName); - entry.IsUnicodeText = useUnicodeText; - entry.CompressionMethod = compressionMethod; - - AddUpdate(new ZipUpdate(fileName, entry)); - } - - /// - /// Add a new entry to the archive. - /// - /// The name of the file to add. - /// The compression method to use. - /// ZipFile has been closed. - /// Compression method is not supported for creating entries. - public void Add(string fileName, CompressionMethod compressionMethod) - { - if (fileName == null) - { - throw new ArgumentNullException(nameof(fileName)); - } - - CheckSupportedCompressionMethod(compressionMethod); - CheckUpdating(); - contentsEdited_ = true; - - ZipEntry entry = EntryFactory.MakeFileEntry(fileName); - entry.CompressionMethod = compressionMethod; - AddUpdate(new ZipUpdate(fileName, entry)); - } - - /// - /// Add a file to the archive. - /// - /// The name of the file to add. - /// Argument supplied is null. - public void Add(string fileName) - { - if (fileName == null) - { - throw new ArgumentNullException(nameof(fileName)); - } - - CheckUpdating(); - AddUpdate(new ZipUpdate(fileName, EntryFactory.MakeFileEntry(fileName))); - } - - /// - /// Add a file to the archive. - /// - /// The name of the file to add. - /// The name to use for the on the Zip file created. - /// Argument supplied is null. - public void Add(string fileName, string entryName) - { - if (fileName == null) - { - throw new ArgumentNullException(nameof(fileName)); - } - - if (entryName == null) - { - throw new ArgumentNullException(nameof(entryName)); - } - - CheckUpdating(); - AddUpdate(new ZipUpdate(fileName, EntryFactory.MakeFileEntry(fileName, entryName, true))); - } - - /// - /// Add a file entry with data. - /// - /// The source of the data for this entry. - /// The name to give to the entry. - public void Add(IStaticDataSource dataSource, string entryName) - { - if (dataSource == null) - { - throw new ArgumentNullException(nameof(dataSource)); - } - - if (entryName == null) - { - throw new ArgumentNullException(nameof(entryName)); - } - - CheckUpdating(); - AddUpdate(new ZipUpdate(dataSource, EntryFactory.MakeFileEntry(entryName, false))); - } - - /// - /// Add a file entry with data. - /// - /// The source of the data for this entry. - /// The name to give to the entry. - /// The compression method to use. - /// Compression method is not supported for creating entries. - public void Add(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod) - { - if (dataSource == null) - { - throw new ArgumentNullException(nameof(dataSource)); - } - - if (entryName == null) - { - throw new ArgumentNullException(nameof(entryName)); - } - - CheckSupportedCompressionMethod(compressionMethod); - CheckUpdating(); - - ZipEntry entry = EntryFactory.MakeFileEntry(entryName, false); - entry.CompressionMethod = compressionMethod; - - AddUpdate(new ZipUpdate(dataSource, entry)); - } - - /// - /// Add a file entry with data. - /// - /// The source of the data for this entry. - /// The name to give to the entry. - /// The compression method to use. - /// Ensure Unicode text is used for name and comments for this entry. - /// Compression method is not supported for creating entries. - public void Add(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod, bool useUnicodeText) - { - if (dataSource == null) - { - throw new ArgumentNullException(nameof(dataSource)); - } - - if (entryName == null) - { - throw new ArgumentNullException(nameof(entryName)); - } - - CheckSupportedCompressionMethod(compressionMethod); - CheckUpdating(); - - ZipEntry entry = EntryFactory.MakeFileEntry(entryName, false); - entry.IsUnicodeText = useUnicodeText; - entry.CompressionMethod = compressionMethod; - - AddUpdate(new ZipUpdate(dataSource, entry)); - } - - /// - /// Add a that contains no data. - /// - /// The entry to add. - /// This can be used to add directories, volume labels, or empty file entries. - public void Add(ZipEntry entry) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - CheckUpdating(); - - if ((entry.Size != 0) || (entry.CompressedSize != 0)) - { - throw new ZipException("Entry cannot have any data"); - } - - AddUpdate(new ZipUpdate(UpdateCommand.Add, entry)); - } - - /// - /// Add a with data. - /// - /// The source of the data for this entry. - /// The entry to add. - /// This can be used to add file entries with a custom data source. - /// - /// The encryption method specified in is unsupported. - /// - /// Compression method is not supported for creating entries. - public void Add(IStaticDataSource dataSource, ZipEntry entry) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - if (dataSource == null) - { - throw new ArgumentNullException(nameof(dataSource)); - } - - // We don't currently support adding entries with AES encryption, so throw - // up front instead of failing or falling back to ZipCrypto later on - if (entry.AESKeySize > 0) - { - throw new NotSupportedException("Creation of AES encrypted entries is not supported"); - } - - CheckSupportedCompressionMethod(entry.CompressionMethod); - CheckUpdating(); - - AddUpdate(new ZipUpdate(dataSource, entry)); - } - - /// - /// Add a directory entry to the archive. - /// - /// The directory to add. - public void AddDirectory(string directoryName) - { - if (directoryName == null) - { - throw new ArgumentNullException(nameof(directoryName)); - } - - CheckUpdating(); - - ZipEntry dirEntry = EntryFactory.MakeDirectoryEntry(directoryName); - AddUpdate(new ZipUpdate(UpdateCommand.Add, dirEntry)); - } - - /// - /// Check if the specified compression method is supported for adding a new entry. - /// - /// The compression method for the new entry. - private static void CheckSupportedCompressionMethod(CompressionMethod compressionMethod) - { - if (compressionMethod != CompressionMethod.Deflated && compressionMethod != CompressionMethod.Stored && compressionMethod != CompressionMethod.BZip2) - { - throw new NotImplementedException("Compression method not supported"); - } - } - - #endregion Adding Entries - - #region Modifying Entries - - /* Modify not yet ready for public consumption. - Direct modification of an entry should not overwrite original data before its read. - Safe mode is trivial in this sense. - public void Modify(ZipEntry original, ZipEntry updated) - { - if ( original == null ) { - throw new ArgumentNullException("original"); - } - if ( updated == null ) { - throw new ArgumentNullException("updated"); - } - CheckUpdating(); - contentsEdited_ = true; - updates_.Add(new ZipUpdate(original, updated)); - } - */ - - #endregion Modifying Entries - - #region Deleting Entries - - /// - /// Delete an entry by name - /// - /// The filename to delete - /// True if the entry was found and deleted; false otherwise. - public bool Delete(string fileName) - { - if (fileName == null) - { - throw new ArgumentNullException(nameof(fileName)); - } - - CheckUpdating(); - - bool result = false; - int index = FindExistingUpdate(fileName); - if ((index >= 0) && (updates_[index] != null)) - { - result = true; - contentsEdited_ = true; - updates_[index] = null; - updateCount_ -= 1; - } - else - { - throw new ZipException("Cannot find entry to delete"); - } - return result; - } - - /// - /// Delete a from the archive. - /// - /// The entry to delete. - public void Delete(ZipEntry entry) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - CheckUpdating(); - - int index = FindExistingUpdate(entry); - if (index >= 0) - { - contentsEdited_ = true; - updates_[index] = null; - updateCount_ -= 1; - } - else - { - throw new ZipException("Cannot find entry to delete"); - } - } - - #endregion Deleting Entries - - #region Update Support - - #region Writing Values/Headers - - private void WriteLEShort(int value) - { - baseStream_.WriteByte((byte)(value & 0xff)); - baseStream_.WriteByte((byte)((value >> 8) & 0xff)); - } - - /// - /// Write an unsigned short in little endian byte order. - /// - private void WriteLEUshort(ushort value) - { - baseStream_.WriteByte((byte)(value & 0xff)); - baseStream_.WriteByte((byte)(value >> 8)); - } - - /// - /// Write an int in little endian byte order. - /// - private void WriteLEInt(int value) - { - WriteLEShort(value & 0xffff); - WriteLEShort(value >> 16); - } - - /// - /// Write an unsigned int in little endian byte order. - /// - private void WriteLEUint(uint value) - { - WriteLEUshort((ushort)(value & 0xffff)); - WriteLEUshort((ushort)(value >> 16)); - } - - /// - /// Write a long in little endian byte order. - /// - private void WriteLeLong(long value) - { - WriteLEInt((int)(value & 0xffffffff)); - WriteLEInt((int)(value >> 32)); - } - - private void WriteLEUlong(ulong value) - { - WriteLEUint((uint)(value & 0xffffffff)); - WriteLEUint((uint)(value >> 32)); - } - - private void WriteLocalEntryHeader(ZipUpdate update) - { - ZipEntry entry = update.OutEntry; - - // TODO: Local offset will require adjusting for multi-disk zip files. - entry.Offset = baseStream_.Position; - - // TODO: Need to clear any entry flags that dont make sense or throw an exception here. - if (update.Command != UpdateCommand.Copy) - { - if (entry.CompressionMethod == CompressionMethod.Deflated) - { - if (entry.Size == 0) - { - // No need to compress - no data. - entry.CompressedSize = entry.Size; - entry.Crc = 0; - entry.CompressionMethod = CompressionMethod.Stored; - } - } - else if (entry.CompressionMethod == CompressionMethod.Stored) - { - entry.Flags &= ~(int)GeneralBitFlags.Descriptor; - } - - if (HaveKeys) - { - entry.IsCrypted = true; - if (entry.Crc < 0) - { - entry.Flags |= (int)GeneralBitFlags.Descriptor; - } - } - else - { - entry.IsCrypted = false; - } - - switch (useZip64_) - { - case UseZip64.Dynamic: - if (entry.Size < 0) - { - entry.ForceZip64(); - } - break; - - case UseZip64.On: - entry.ForceZip64(); - break; - - case UseZip64.Off: - // Do nothing. The entry itself may be using Zip64 independently. - break; - } - } - - // Write the local file header - WriteLEInt(ZipConstants.LocalHeaderSignature); - - WriteLEShort(entry.Version); - WriteLEShort(entry.Flags); - - WriteLEShort((byte)entry.CompressionMethodForHeader); - WriteLEInt((int)entry.DosTime); - - if (!entry.HasCrc) - { - // Note patch address for updating CRC later. - update.CrcPatchOffset = baseStream_.Position; - WriteLEInt((int)0); - } - else - { - WriteLEInt(unchecked((int)entry.Crc)); - } - - if (entry.LocalHeaderRequiresZip64) - { - WriteLEInt(-1); - WriteLEInt(-1); - } - else - { - if ((entry.CompressedSize < 0) || (entry.Size < 0)) - { - update.SizePatchOffset = baseStream_.Position; - } - - WriteLEInt((int)entry.CompressedSize); - WriteLEInt((int)entry.Size); - } - - var entryEncoding = _stringCodec.ZipInputEncoding(entry.Flags); - byte[] name = entryEncoding.GetBytes(entry.Name); - - if (name.Length > 0xFFFF) - { - throw new ZipException("Entry name too long."); - } - - var ed = new ZipExtraData(entry.ExtraData); - - if (entry.LocalHeaderRequiresZip64) - { - ed.StartNewEntry(); - - // Local entry header always includes size and compressed size. - // NOTE the order of these fields is reversed when compared to the normal headers! - ed.AddLeLong(entry.Size); - ed.AddLeLong(entry.CompressedSize); - ed.AddNewEntry(1); - } - else - { - ed.Delete(1); - } - - entry.ExtraData = ed.GetEntryData(); - - WriteLEShort(name.Length); - WriteLEShort(entry.ExtraData.Length); - - if (name.Length > 0) - { - baseStream_.Write(name, 0, name.Length); - } - - if (entry.LocalHeaderRequiresZip64) - { - if (!ed.Find(1)) - { - throw new ZipException("Internal error cannot find extra data"); - } - - update.SizePatchOffset = baseStream_.Position + ed.CurrentReadIndex; - } - - if (entry.ExtraData.Length > 0) - { - baseStream_.Write(entry.ExtraData, 0, entry.ExtraData.Length); - } - } - - private int WriteCentralDirectoryHeader(ZipEntry entry) - { - if (entry.CompressedSize < 0) - { - throw new ZipException("Attempt to write central directory entry with unknown csize"); - } - - if (entry.Size < 0) - { - throw new ZipException("Attempt to write central directory entry with unknown size"); - } - - if (entry.Crc < 0) - { - throw new ZipException("Attempt to write central directory entry with unknown crc"); - } - - // Write the central file header - WriteLEInt(ZipConstants.CentralHeaderSignature); - - // Version made by - WriteLEShort((entry.HostSystem << 8) | entry.VersionMadeBy); - - // Version required to extract - WriteLEShort(entry.Version); - - WriteLEShort(entry.Flags); - - unchecked - { - WriteLEShort((byte)entry.CompressionMethodForHeader); - WriteLEInt((int)entry.DosTime); - WriteLEInt((int)entry.Crc); - } - - bool useExtraCompressedSize = false; //Do we want to store the compressed size in the extra data? - if ((entry.IsZip64Forced()) || (entry.CompressedSize >= 0xffffffff)) - { - useExtraCompressedSize = true; - WriteLEInt(-1); - } - else - { - WriteLEInt((int)(entry.CompressedSize & 0xffffffff)); - } - - bool useExtraUncompressedSize = false; //Do we want to store the uncompressed size in the extra data? - if ((entry.IsZip64Forced()) || (entry.Size >= 0xffffffff)) - { - useExtraUncompressedSize = true; - WriteLEInt(-1); - } - else - { - WriteLEInt((int)entry.Size); - } - - var entryEncoding = _stringCodec.ZipInputEncoding(entry.Flags); - byte[] name = entryEncoding.GetBytes(entry.Name); - - if (name.Length > 0xFFFF) - { - throw new ZipException("Entry name is too long."); - } - - WriteLEShort(name.Length); - - // Central header extra data is different to local header version so regenerate. - var ed = new ZipExtraData(entry.ExtraData); - - if (entry.CentralHeaderRequiresZip64) - { - ed.StartNewEntry(); - - if (useExtraUncompressedSize) - { - ed.AddLeLong(entry.Size); - } - - if (useExtraCompressedSize) - { - ed.AddLeLong(entry.CompressedSize); - } - - if (entry.Offset >= 0xffffffff) - { - ed.AddLeLong(entry.Offset); - } - - // Number of disk on which this file starts isnt supported and is never written here. - ed.AddNewEntry(1); - } - else - { - // Should have already be done when local header was added. - ed.Delete(1); - } - - byte[] centralExtraData = ed.GetEntryData(); - - WriteLEShort(centralExtraData.Length); - WriteLEShort(entry.Comment != null ? entry.Comment.Length : 0); - - WriteLEShort(0); // disk number - WriteLEShort(0); // internal file attributes - - // External file attributes... - if (entry.ExternalFileAttributes != -1) - { - WriteLEInt(entry.ExternalFileAttributes); - } - else - { - if (entry.IsDirectory) - { - WriteLEUint(16); - } - else - { - WriteLEUint(0); - } - } - - if (entry.Offset >= 0xffffffff) - { - WriteLEUint(0xffffffff); - } - else - { - WriteLEUint((uint)(int)entry.Offset); - } - - if (name.Length > 0) - { - baseStream_.Write(name, 0, name.Length); - } - - if (centralExtraData.Length > 0) - { - baseStream_.Write(centralExtraData, 0, centralExtraData.Length); - } - - byte[] rawComment = (entry.Comment != null) ? Encoding.ASCII.GetBytes(entry.Comment) : Empty.Array(); - - if (rawComment.Length > 0) - { - baseStream_.Write(rawComment, 0, rawComment.Length); - } - - return ZipConstants.CentralHeaderBaseSize + name.Length + centralExtraData.Length + rawComment.Length; - } - - #endregion Writing Values/Headers - - private void PostUpdateCleanup() - { - updateDataSource_ = null; - updates_ = null; - updateIndex_ = null; - - if (archiveStorage_ != null) - { - archiveStorage_.Dispose(); - archiveStorage_ = null; - } - } - - private string GetTransformedFileName(string name) - { - INameTransform transform = NameTransform; - return (transform != null) ? - transform.TransformFile(name) : - name; - } - - private string GetTransformedDirectoryName(string name) - { - INameTransform transform = NameTransform; - return (transform != null) ? - transform.TransformDirectory(name) : - name; - } - - /// - /// Get a raw memory buffer. - /// - /// Returns a raw memory buffer. - private byte[] GetBuffer() - { - if (copyBuffer_ == null) - { - copyBuffer_ = new byte[bufferSize_]; - } - return copyBuffer_; - } - - private void CopyDescriptorBytes(ZipUpdate update, Stream dest, Stream source) - { - // Don't include the signature size to allow copy without seeking - var bytesToCopy = GetDescriptorSize(update, false); - - // Don't touch the source stream if no descriptor is present - if (bytesToCopy == 0) return; - - var buffer = GetBuffer(); - - // Copy the first 4 bytes of the descriptor - source.Read(buffer, 0, sizeof(int)); - dest.Write(buffer, 0, sizeof(int)); - - if (BitConverter.ToUInt32(buffer, 0) != ZipConstants.DataDescriptorSignature) - { - // The initial bytes wasn't the descriptor, reduce the pending byte count - bytesToCopy -= buffer.Length; - } - - while (bytesToCopy > 0) - { - int readSize = Math.Min(buffer.Length, bytesToCopy); - - int bytesRead = source.Read(buffer, 0, readSize); - if (bytesRead > 0) - { - dest.Write(buffer, 0, bytesRead); - bytesToCopy -= bytesRead; - } - else - { - throw new ZipException("Unxpected end of stream"); - } - } - } - - private void CopyBytes(ZipUpdate update, Stream destination, Stream source, - long bytesToCopy, bool updateCrc) - { - if (destination == source) - { - throw new InvalidOperationException("Destination and source are the same"); - } - - // NOTE: Compressed size is updated elsewhere. - var crc = new Crc32(); - byte[] buffer = GetBuffer(); - - long targetBytes = bytesToCopy; - long totalBytesRead = 0; - - int bytesRead; - do - { - int readSize = buffer.Length; - - if (bytesToCopy < readSize) - { - readSize = (int)bytesToCopy; - } - - bytesRead = source.Read(buffer, 0, readSize); - if (bytesRead > 0) - { - if (updateCrc) - { - crc.Update(new ArraySegment(buffer, 0, bytesRead)); - } - destination.Write(buffer, 0, bytesRead); - bytesToCopy -= bytesRead; - totalBytesRead += bytesRead; - } - } - while ((bytesRead > 0) && (bytesToCopy > 0)); - - if (totalBytesRead != targetBytes) - { - throw new ZipException(string.Format("Failed to copy bytes expected {0} read {1}", targetBytes, totalBytesRead)); - } - - if (updateCrc) - { - update.OutEntry.Crc = crc.Value; - } - } - - /// - /// Get the size of the source descriptor for a . - /// - /// The update to get the size for. - /// Whether to include the signature size - /// The descriptor size, zero if there isn't one. - private static int GetDescriptorSize(ZipUpdate update, bool includingSignature) - { - if (!((GeneralBitFlags)update.Entry.Flags).HasFlag(GeneralBitFlags.Descriptor)) - return 0; - - var descriptorWithSignature = update.Entry.LocalHeaderRequiresZip64 - ? ZipConstants.Zip64DataDescriptorSize - : ZipConstants.DataDescriptorSize; - - return includingSignature - ? descriptorWithSignature - : descriptorWithSignature - sizeof(int); - } - - private void CopyDescriptorBytesDirect(ZipUpdate update, Stream stream, ref long destinationPosition, long sourcePosition) - { - var buffer = GetBuffer(); ; - - stream.Position = sourcePosition; - stream.Read(buffer, 0, sizeof(int)); - var sourceHasSignature = BitConverter.ToUInt32(buffer, 0) == ZipConstants.DataDescriptorSignature; - - var bytesToCopy = GetDescriptorSize(update, sourceHasSignature); - - while (bytesToCopy > 0) - { - stream.Position = sourcePosition; - - var bytesRead = stream.Read(buffer, 0, bytesToCopy); - if (bytesRead > 0) - { - stream.Position = destinationPosition; - stream.Write(buffer, 0, bytesRead); - bytesToCopy -= bytesRead; - destinationPosition += bytesRead; - sourcePosition += bytesRead; - } - else - { - throw new ZipException("Unexpected end of stream"); - } - } - } - - private void CopyEntryDataDirect(ZipUpdate update, Stream stream, bool updateCrc, ref long destinationPosition, ref long sourcePosition) - { - long bytesToCopy = update.Entry.CompressedSize; - - // NOTE: Compressed size is updated elsewhere. - var crc = new Crc32(); - byte[] buffer = GetBuffer(); - - long targetBytes = bytesToCopy; - long totalBytesRead = 0; - - int bytesRead; - do - { - int readSize = buffer.Length; - - if (bytesToCopy < readSize) - { - readSize = (int)bytesToCopy; - } - - stream.Position = sourcePosition; - bytesRead = stream.Read(buffer, 0, readSize); - if (bytesRead > 0) - { - if (updateCrc) - { - crc.Update(new ArraySegment(buffer, 0, bytesRead)); - } - stream.Position = destinationPosition; - stream.Write(buffer, 0, bytesRead); - - destinationPosition += bytesRead; - sourcePosition += bytesRead; - bytesToCopy -= bytesRead; - totalBytesRead += bytesRead; - } - } - while ((bytesRead > 0) && (bytesToCopy > 0)); - - if (totalBytesRead != targetBytes) - { - throw new ZipException(string.Format("Failed to copy bytes expected {0} read {1}", targetBytes, totalBytesRead)); - } - - if (updateCrc) - { - update.OutEntry.Crc = crc.Value; - } - } - - private int FindExistingUpdate(ZipEntry entry) - { - int result = -1; - if (updateIndex_.ContainsKey(entry.Name)) - { - result = (int)updateIndex_[entry.Name]; - } - /* - // This is slow like the coming of the next ice age but takes less storage and may be useful - // for CF? - for (int index = 0; index < updates_.Count; ++index) - { - ZipUpdate zu = ( ZipUpdate )updates_[index]; - if ( (zu.Entry.ZipFileIndex == entry.ZipFileIndex) && - (string.Compare(convertedName, zu.Entry.Name, true, CultureInfo.InvariantCulture) == 0) ) { - result = index; - break; - } - } - */ - return result; - } - - private int FindExistingUpdate(string fileName, bool isEntryName = false) - { - int result = -1; - - string convertedName = !isEntryName ? GetTransformedFileName(fileName) : fileName; - - if (updateIndex_.ContainsKey(convertedName)) - { - result = (int)updateIndex_[convertedName]; - } - - /* - // This is slow like the coming of the next ice age but takes less storage and may be useful - // for CF? - for ( int index = 0; index < updates_.Count; ++index ) { - if ( string.Compare(convertedName, (( ZipUpdate )updates_[index]).Entry.Name, - true, CultureInfo.InvariantCulture) == 0 ) { - result = index; - break; - } - } - */ - - return result; - } - - /// - /// Get an output stream for the specified - /// - /// The entry to get an output stream for. - /// The output stream obtained for the entry. - private Stream GetOutputStream(ZipEntry entry) - { - Stream result = baseStream_; - - if (entry.IsCrypted == true) - { - result = CreateAndInitEncryptionStream(result, entry); - } - - switch (entry.CompressionMethod) - { - case CompressionMethod.Stored: - if (!entry.IsCrypted) - { - // If there is an encryption stream in use, that can be returned directly - // otherwise, wrap the base stream in an UncompressedStream instead of returning it directly - result = new UncompressedStream(result); - } - break; - - case CompressionMethod.Deflated: - var dos = new DeflaterOutputStream(result, new Deflater(9, true)) - { - // If there is an encryption stream in use, then we want that to be disposed when the deflator stream is disposed - // If not, then we don't want it to dispose the base stream - IsStreamOwner = entry.IsCrypted - }; - result = dos; - break; - - case CompressionMethod.BZip2: - var bzos = new BZip2.BZip2OutputStream(result) - { - // If there is an encryption stream in use, then we want that to be disposed when the BZip2OutputStream stream is disposed - // If not, then we don't want it to dispose the base stream - IsStreamOwner = entry.IsCrypted - }; - result = bzos; - break; - - default: - throw new ZipException("Unknown compression method " + entry.CompressionMethod); - } - return result; - } - - private void AddEntry(ZipFile workFile, ZipUpdate update) - { - Stream source = null; - - if (update.Entry.IsFile) - { - source = update.GetSource(); - - if (source == null) - { - source = updateDataSource_.GetSource(update.Entry, update.Filename); - } - } - - var useCrc = update.Entry.AESKeySize == 0; - - if (source != null) - { - using (source) - { - long sourceStreamLength = source.Length; - if (update.OutEntry.Size < 0) - { - update.OutEntry.Size = sourceStreamLength; - } - else - { - // Check for errant entries. - if (update.OutEntry.Size != sourceStreamLength) - { - throw new ZipException("Entry size/stream size mismatch"); - } - } - - workFile.WriteLocalEntryHeader(update); - - long dataStart = workFile.baseStream_.Position; - - using (Stream output = workFile.GetOutputStream(update.OutEntry)) - { - CopyBytes(update, output, source, sourceStreamLength, useCrc); - } - - long dataEnd = workFile.baseStream_.Position; - update.OutEntry.CompressedSize = dataEnd - dataStart; - - if ((update.OutEntry.Flags & (int)GeneralBitFlags.Descriptor) == (int)GeneralBitFlags.Descriptor) - { - ZipFormat.WriteDataDescriptor(workFile.baseStream_, update.OutEntry); - } - } - } - else - { - workFile.WriteLocalEntryHeader(update); - update.OutEntry.CompressedSize = 0; - } - } - - private void ModifyEntry(ZipFile workFile, ZipUpdate update) - { - workFile.WriteLocalEntryHeader(update); - long dataStart = workFile.baseStream_.Position; - - // TODO: This is slow if the changes don't effect the data!! - if (update.Entry.IsFile && (update.Filename != null)) - { - using (Stream output = workFile.GetOutputStream(update.OutEntry)) - { - using (Stream source = this.GetInputStream(update.Entry)) - { - CopyBytes(update, output, source, source.Length, true); - } - } - } - - long dataEnd = workFile.baseStream_.Position; - update.Entry.CompressedSize = dataEnd - dataStart; - } - - private void CopyEntryDirect(ZipFile workFile, ZipUpdate update, ref long destinationPosition) - { - bool skipOver = false || update.Entry.Offset == destinationPosition; - - if (!skipOver) - { - baseStream_.Position = destinationPosition; - workFile.WriteLocalEntryHeader(update); - destinationPosition = baseStream_.Position; - } - - long sourcePosition = 0; - - const int NameLengthOffset = 26; - - // TODO: Add base for SFX friendly handling - long entryDataOffset = update.Entry.Offset + NameLengthOffset; - - baseStream_.Seek(entryDataOffset, SeekOrigin.Begin); - - // Clumsy way of handling retrieving the original name and extra data length for now. - // TODO: Stop re-reading name and data length in CopyEntryDirect. - - uint nameLength = ReadLEUshort(); - uint extraLength = ReadLEUshort(); - - sourcePosition = baseStream_.Position + nameLength + extraLength; - - if (skipOver) - { - if (update.OffsetBasedSize != -1) - { - destinationPosition += update.OffsetBasedSize; - } - else - { - // Skip entry header - destinationPosition += (sourcePosition - entryDataOffset) + NameLengthOffset; - - // Skip entry compressed data - destinationPosition += update.Entry.CompressedSize; - - // Seek to end of entry to check for descriptor signature - baseStream_.Seek(destinationPosition, SeekOrigin.Begin); - - var descriptorHasSignature = ReadLEUint() == ZipConstants.DataDescriptorSignature; - - // Skip descriptor and it's signature (if present) - destinationPosition += GetDescriptorSize(update, descriptorHasSignature); - } - } - else - { - if (update.Entry.CompressedSize > 0) - { - CopyEntryDataDirect(update, baseStream_, false, ref destinationPosition, ref sourcePosition); - } - CopyDescriptorBytesDirect(update, baseStream_, ref destinationPosition, sourcePosition); - } - } - - private void CopyEntry(ZipFile workFile, ZipUpdate update) - { - workFile.WriteLocalEntryHeader(update); - - if (update.Entry.CompressedSize > 0) - { - const int NameLengthOffset = 26; - - long entryDataOffset = update.Entry.Offset + NameLengthOffset; - - // TODO: This wont work for SFX files! - baseStream_.Seek(entryDataOffset, SeekOrigin.Begin); - - uint nameLength = ReadLEUshort(); - uint extraLength = ReadLEUshort(); - - baseStream_.Seek(nameLength + extraLength, SeekOrigin.Current); - - CopyBytes(update, workFile.baseStream_, baseStream_, update.Entry.CompressedSize, false); - } - CopyDescriptorBytes(update, workFile.baseStream_, baseStream_); - } - - private void Reopen(Stream source) - { - isNewArchive_ = false; - baseStream_ = source ?? throw new ZipException("Failed to reopen archive - no source"); - ReadEntries(); - } - - private void Reopen() - { - if (Name == null) - { - throw new InvalidOperationException("Name is not known cannot Reopen"); - } - - Reopen(File.Open(Name, FileMode.Open, FileAccess.Read, FileShare.Read)); - } - - private void UpdateCommentOnly() - { - long baseLength = baseStream_.Length; - - Stream updateFile; - - if (archiveStorage_.UpdateMode == FileUpdateMode.Safe) - { - updateFile = archiveStorage_.MakeTemporaryCopy(baseStream_); - - baseStream_.Dispose(); - baseStream_ = null; - } - else - { - if (archiveStorage_.UpdateMode == FileUpdateMode.Direct) - { - // TODO: archiveStorage wasnt originally intended for this use. - // Need to revisit this to tidy up handling as archive storage currently doesnt - // handle the original stream well. - // The problem is when using an existing zip archive with an in memory archive storage. - // The open stream wont support writing but the memory storage should open the same file not an in memory one. - - // Need to tidy up the archive storage interface and contract basically. - baseStream_ = archiveStorage_.OpenForDirectUpdate(baseStream_); - updateFile = baseStream_; - } - else - { - baseStream_.Dispose(); - baseStream_ = null; - updateFile = new FileStream(Name, FileMode.Open, FileAccess.ReadWrite); - } - } - - try - { - long locatedCentralDirOffset = - ZipFormat.LocateBlockWithSignature(updateFile, ZipConstants.EndOfCentralDirectorySignature, - baseLength, ZipConstants.EndOfCentralRecordBaseSize, 0xffff); - if (locatedCentralDirOffset < 0) - { - throw new ZipException("Cannot find central directory"); - } - - const int CentralHeaderCommentSizeOffset = 16; - updateFile.Position += CentralHeaderCommentSizeOffset; - - byte[] rawComment = newComment_.RawComment; - - updateFile.WriteLEShort(rawComment.Length); - updateFile.Write(rawComment, 0, rawComment.Length); - updateFile.SetLength(updateFile.Position); - } - finally - { - if (updateFile != baseStream_) - updateFile.Dispose(); - } - - if (archiveStorage_.UpdateMode == FileUpdateMode.Safe) - { - Reopen(archiveStorage_.ConvertTemporaryToFinal()); - } - else - { - ReadEntries(); - } - } - - /// - /// Class used to sort updates. - /// - private class UpdateComparer : IComparer - { - /// - /// Compares two objects and returns a value indicating whether one is - /// less than, equal to or greater than the other. - /// - /// First object to compare - /// Second object to compare. - /// Compare result. - public int Compare(ZipUpdate x, ZipUpdate y) - { - int result; - - if (x == null) - { - if (y == null) - { - result = 0; - } - else - { - result = -1; - } - } - else if (y == null) - { - result = 1; - } - else - { - int xCmdValue = ((x.Command == UpdateCommand.Copy) || (x.Command == UpdateCommand.Modify)) ? 0 : 1; - int yCmdValue = ((y.Command == UpdateCommand.Copy) || (y.Command == UpdateCommand.Modify)) ? 0 : 1; - - result = xCmdValue - yCmdValue; - if (result == 0) - { - long offsetDiff = x.Entry.Offset - y.Entry.Offset; - if (offsetDiff < 0) - { - result = -1; - } - else if (offsetDiff == 0) - { - result = 0; - } - else - { - result = 1; - } - } - } - return result; - } - } - - private void RunUpdates() - { - long sizeEntries = 0; - long endOfStream = 0; - bool directUpdate = false; - long destinationPosition = 0; // NOT SFX friendly - - ZipFile workFile; - - if (IsNewArchive) - { - workFile = this; - workFile.baseStream_.Position = 0; - directUpdate = true; - } - else if (archiveStorage_.UpdateMode == FileUpdateMode.Direct) - { - workFile = this; - workFile.baseStream_.Position = 0; - directUpdate = true; - - // Sort the updates by offset within copies/modifies, then adds. - // This ensures that data required by copies will not be overwritten. - updates_.Sort(new UpdateComparer()); - } - else - { - workFile = ZipFile.Create(archiveStorage_.GetTemporaryOutput()); - workFile.UseZip64 = UseZip64; - - if (key != null) - { - workFile.key = (byte[])key.Clone(); - } - } - - try - { - foreach (ZipUpdate update in updates_) - { - if (update != null) - { - switch (update.Command) - { - case UpdateCommand.Copy: - if (directUpdate) - { - CopyEntryDirect(workFile, update, ref destinationPosition); - } - else - { - CopyEntry(workFile, update); - } - break; - - case UpdateCommand.Modify: - // TODO: Direct modifying of an entry will take some legwork. - ModifyEntry(workFile, update); - break; - - case UpdateCommand.Add: - if (!IsNewArchive && directUpdate) - { - workFile.baseStream_.Position = destinationPosition; - } - - AddEntry(workFile, update); - - if (directUpdate) - { - destinationPosition = workFile.baseStream_.Position; - } - break; - } - } - } - - if (!IsNewArchive && directUpdate) - { - workFile.baseStream_.Position = destinationPosition; - } - - long centralDirOffset = workFile.baseStream_.Position; - - foreach (ZipUpdate update in updates_) - { - if (update != null) - { - sizeEntries += workFile.WriteCentralDirectoryHeader(update.OutEntry); - } - } - - byte[] theComment = newComment_?.RawComment ?? _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment_); - ZipFormat.WriteEndOfCentralDirectory(workFile.baseStream_, updateCount_, - sizeEntries, centralDirOffset, theComment); - - endOfStream = workFile.baseStream_.Position; - - // And now patch entries... - foreach (ZipUpdate update in updates_) - { - if (update != null) - { - // If the size of the entry is zero leave the crc as 0 as well. - // The calculated crc will be all bits on... - if ((update.CrcPatchOffset > 0) && (update.OutEntry.CompressedSize > 0)) - { - workFile.baseStream_.Position = update.CrcPatchOffset; - workFile.WriteLEInt((int)update.OutEntry.Crc); - } - - if (update.SizePatchOffset > 0) - { - workFile.baseStream_.Position = update.SizePatchOffset; - if (update.OutEntry.LocalHeaderRequiresZip64) - { - workFile.WriteLeLong(update.OutEntry.Size); - workFile.WriteLeLong(update.OutEntry.CompressedSize); - } - else - { - workFile.WriteLEInt((int)update.OutEntry.CompressedSize); - workFile.WriteLEInt((int)update.OutEntry.Size); - } - } - } - } - } - catch - { - workFile.Close(); - if (!directUpdate && (workFile.Name != null)) - { - File.Delete(workFile.Name); - } - throw; - } - - if (directUpdate) - { - workFile.baseStream_.SetLength(endOfStream); - workFile.baseStream_.Flush(); - isNewArchive_ = false; - ReadEntries(); - } - else - { - baseStream_.Dispose(); - Reopen(archiveStorage_.ConvertTemporaryToFinal()); - } - } - - private void CheckUpdating() - { - if (updates_ == null) - { - throw new InvalidOperationException("BeginUpdate has not been called"); - } - } - - #endregion Update Support - - #region ZipUpdate class - - /// - /// Represents a pending update to a Zip file. - /// - private class ZipUpdate - { - #region Constructors - - public ZipUpdate(string fileName, ZipEntry entry) - { - command_ = UpdateCommand.Add; - entry_ = entry; - filename_ = fileName; - } - - [Obsolete] - public ZipUpdate(string fileName, string entryName, CompressionMethod compressionMethod) - { - command_ = UpdateCommand.Add; - entry_ = new ZipEntry(entryName) - { - CompressionMethod = compressionMethod - }; - filename_ = fileName; - } - - [Obsolete] - public ZipUpdate(string fileName, string entryName) - : this(fileName, entryName, CompressionMethod.Deflated) - { - // Do nothing. - } - - [Obsolete] - public ZipUpdate(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod) - { - command_ = UpdateCommand.Add; - entry_ = new ZipEntry(entryName) - { - CompressionMethod = compressionMethod - }; - dataSource_ = dataSource; - } - - public ZipUpdate(IStaticDataSource dataSource, ZipEntry entry) - { - command_ = UpdateCommand.Add; - entry_ = entry; - dataSource_ = dataSource; - } - - public ZipUpdate(ZipEntry original, ZipEntry updated) - { - throw new ZipException("Modify not currently supported"); - /* - command_ = UpdateCommand.Modify; - entry_ = ( ZipEntry )original.Clone(); - outEntry_ = ( ZipEntry )updated.Clone(); - */ - } - - public ZipUpdate(UpdateCommand command, ZipEntry entry) - { - command_ = command; - entry_ = (ZipEntry)entry.Clone(); - } - - /// - /// Copy an existing entry. - /// - /// The existing entry to copy. - public ZipUpdate(ZipEntry entry) - : this(UpdateCommand.Copy, entry) - { - // Do nothing. - } - - #endregion Constructors - - /// - /// Get the for this update. - /// - /// This is the source or original entry. - public ZipEntry Entry - { - get { return entry_; } - } - - /// - /// Get the that will be written to the updated/new file. - /// - public ZipEntry OutEntry - { - get - { - if (outEntry_ == null) - { - outEntry_ = (ZipEntry)entry_.Clone(); - } - - return outEntry_; - } - } - - /// - /// Get the command for this update. - /// - public UpdateCommand Command - { - get { return command_; } - } - - /// - /// Get the filename if any for this update. Null if none exists. - /// - public string Filename - { - get { return filename_; } - } - - /// - /// Get/set the location of the size patch for this update. - /// - public long SizePatchOffset - { - get { return sizePatchOffset_; } - set { sizePatchOffset_ = value; } - } - - /// - /// Get /set the location of the crc patch for this update. - /// - public long CrcPatchOffset - { - get { return crcPatchOffset_; } - set { crcPatchOffset_ = value; } - } - - /// - /// Get/set the size calculated by offset. - /// Specifically, the difference between this and next entry's starting offset. - /// - public long OffsetBasedSize - { - get { return _offsetBasedSize; } - set { _offsetBasedSize = value; } - } - - public Stream GetSource() - { - Stream result = null; - if (dataSource_ != null) - { - result = dataSource_.GetSource(); - } - - return result; - } - - #region Instance Fields - - private ZipEntry entry_; - private ZipEntry outEntry_; - private readonly UpdateCommand command_; - private IStaticDataSource dataSource_; - private readonly string filename_; - private long sizePatchOffset_ = -1; - private long crcPatchOffset_ = -1; - private long _offsetBasedSize = -1; - - #endregion Instance Fields - } - - #endregion ZipUpdate class - - #endregion Updating - - #region Disposing - - #region IDisposable Members - - void IDisposable.Dispose() - { - Close(); - } - - #endregion IDisposable Members - - private void DisposeInternal(bool disposing) - { - if (!isDisposed_) - { - isDisposed_ = true; - entries_ = Empty.Array(); - - if (IsStreamOwner && (baseStream_ != null)) - { - lock (baseStream_) - { - baseStream_.Dispose(); - } - } - - PostUpdateCleanup(); - } - } - - /// - /// Releases the unmanaged resources used by the this instance and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - DisposeInternal(disposing); - } - - #endregion Disposing - - #region Internal routines - - #region Reading - - /// - /// Read an unsigned short in little endian byte order. - /// - /// Returns the value read. - /// - /// The stream ends prematurely - /// - private ushort ReadLEUshort() - { - int data1 = baseStream_.ReadByte(); - - if (data1 < 0) - { - throw new EndOfStreamException("End of stream"); - } - - int data2 = baseStream_.ReadByte(); - - if (data2 < 0) - { - throw new EndOfStreamException("End of stream"); - } - - return unchecked((ushort)((ushort)data1 | (ushort)(data2 << 8))); - } - - /// - /// Read a uint in little endian byte order. - /// - /// Returns the value read. - /// - /// An i/o error occurs. - /// - /// - /// The file ends prematurely - /// - private uint ReadLEUint() - { - return (uint)(ReadLEUshort() | (ReadLEUshort() << 16)); - } - - private ulong ReadLEUlong() - { - return ReadLEUint() | ((ulong)ReadLEUint() << 32); - } - - #endregion Reading - - // NOTE this returns the offset of the first byte after the signature. - private long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData) - => ZipFormat.LocateBlockWithSignature(baseStream_, signature, endLocation, minimumBlockSize, maximumVariableData); - - /// - /// - /// - /// - public long GetCentralDirOffset() - { - // Search for the End Of Central Directory. When a zip comment is - // present the directory will start earlier - // - // The search is limited to 64K which is the maximum size of a trailing comment field to aid speed. - // This should be compatible with both SFX and ZIP files but has only been tested for Zip files - // If a SFX file has the Zip data attached as a resource and there are other resources occurring later then - // this could be invalid. - // Could also speed this up by reading memory in larger blocks. - - if (baseStream_.CanSeek == false) - { - throw new ZipException("ZipFile stream must be seekable"); - } - - long locatedEndOfCentralDir = LocateBlockWithSignature(ZipConstants.EndOfCentralDirectorySignature, - baseStream_.Length, ZipConstants.EndOfCentralRecordBaseSize, 0xffff); - - if (locatedEndOfCentralDir < 0) - { - throw new ZipException("Cannot find central directory"); - } - - // Read end of central directory record - ushort thisDiskNumber = ReadLEUshort(); - ushort startCentralDirDisk = ReadLEUshort(); - ulong entriesForThisDisk = ReadLEUshort(); - ulong entriesForWholeCentralDir = ReadLEUshort(); - ulong centralDirSize = ReadLEUint(); - long offsetOfCentralDir = ReadLEUint(); - uint commentSize = ReadLEUshort(); - - if (commentSize > 0) - { - byte[] comment = new byte[commentSize]; - - StreamUtils.ReadFully(baseStream_, comment); - comment_ = _stringCodec.ZipArchiveCommentEncoding.GetString(comment); - } - else - { - comment_ = string.Empty; - } - - bool isZip64 = false; - bool requireZip64 = false; - - // Check if zip64 header information is required. - if ((thisDiskNumber == 0xffff) || - (startCentralDirDisk == 0xffff) || - (entriesForThisDisk == 0xffff) || - (entriesForWholeCentralDir == 0xffff) || - (centralDirSize == 0xffffffff) || - (offsetOfCentralDir == 0xffffffff)) - { - requireZip64 = true; - } - - // #357 - always check for the existance of the Zip64 central directory. - // #403 - Take account of the fixed size of the locator when searching. - // Subtract from locatedEndOfCentralDir so that the endLocation is the location of EndOfCentralDirectorySignature, - // rather than the data following the signature. - long locatedZip64EndOfCentralDirLocator = LocateBlockWithSignature( - ZipConstants.Zip64CentralDirLocatorSignature, - locatedEndOfCentralDir - 4, - ZipConstants.Zip64EndOfCentralDirectoryLocatorSize, - 0); - - if (locatedZip64EndOfCentralDirLocator < 0) - { - if (requireZip64) - { - // This is only an error in cases where the Zip64 directory is required. - throw new ZipException("Cannot find Zip64 locator"); - } - } - else - { - isZip64 = true; - - // 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 - ReadLEUint(); // startDisk64 is not currently used - ulong offset64 = ReadLEUlong(); - uint totalDisks = ReadLEUint(); - - baseStream_.Position = (long)offset64; - long sig64 = ReadLEUint(); - - if (sig64 != ZipConstants.Zip64CentralFileHeaderSignature) - { - throw new ZipException(string.Format("Invalid Zip64 Central directory signature at {0:X}", offset64)); - } - - // NOTE: Record size = SizeOfFixedFields + SizeOfVariableData - 12. - ulong recordSize = ReadLEUlong(); - int versionMadeBy = ReadLEUshort(); - int versionToExtract = ReadLEUshort(); - uint thisDisk = ReadLEUint(); - uint centralDirDisk = ReadLEUint(); - entriesForThisDisk = ReadLEUlong(); - entriesForWholeCentralDir = ReadLEUlong(); - centralDirSize = ReadLEUlong(); - offsetOfCentralDir = (long)ReadLEUlong(); - - // NOTE: zip64 extensible data sector (variable size) is ignored. - } - - if (!isZip64 && (offsetOfCentralDir < locatedEndOfCentralDir - (4 + (long)centralDirSize))) - { - offsetOfFirstEntry = locatedEndOfCentralDir - (4 + (long)centralDirSize + offsetOfCentralDir); - if (offsetOfFirstEntry <= 0) - { - throw new ZipException("Invalid embedded zip archive"); - } - } - - return offsetOfFirstEntry + offsetOfCentralDir; - } - - /// - /// Search for and read the central directory of a zip file filling the entries array. - /// - /// - /// An i/o error occurs. - /// - /// - /// The central directory is malformed or cannot be found - /// - private void ReadEntries() - { - // Search for the End Of Central Directory. When a zip comment is - // present the directory will start earlier - // - // The search is limited to 64K which is the maximum size of a trailing comment field to aid speed. - // This should be compatible with both SFX and ZIP files but has only been tested for Zip files - // If a SFX file has the Zip data attached as a resource and there are other resources occurring later then - // this could be invalid. - // Could also speed this up by reading memory in larger blocks. - - if (baseStream_.CanSeek == false) - { - throw new ZipException("ZipFile stream must be seekable"); - } - - long locatedEndOfCentralDir = LocateBlockWithSignature(ZipConstants.EndOfCentralDirectorySignature, - baseStream_.Length, ZipConstants.EndOfCentralRecordBaseSize, 0xffff); - - if (locatedEndOfCentralDir < 0) - { - throw new ZipException("Cannot find central directory"); - } - - // Read end of central directory record - ushort thisDiskNumber = ReadLEUshort(); - ushort startCentralDirDisk = ReadLEUshort(); - ulong entriesForThisDisk = ReadLEUshort(); - ulong entriesForWholeCentralDir = ReadLEUshort(); - ulong centralDirSize = ReadLEUint(); - long offsetOfCentralDir = ReadLEUint(); - uint commentSize = ReadLEUshort(); - - if (commentSize > 0) - { - byte[] comment = new byte[commentSize]; - - StreamUtils.ReadFully(baseStream_, comment); - comment_ = _stringCodec.ZipArchiveCommentEncoding.GetString(comment); - } - else - { - comment_ = string.Empty; - } - - bool isZip64 = false; - bool requireZip64 = false; - - // Check if zip64 header information is required. - if ((thisDiskNumber == 0xffff) || - (startCentralDirDisk == 0xffff) || - (entriesForThisDisk == 0xffff) || - (entriesForWholeCentralDir == 0xffff) || - (centralDirSize == 0xffffffff) || - (offsetOfCentralDir == 0xffffffff)) - { - requireZip64 = true; - } - - // #357 - always check for the existance of the Zip64 central directory. - // #403 - Take account of the fixed size of the locator when searching. - // Subtract from locatedEndOfCentralDir so that the endLocation is the location of EndOfCentralDirectorySignature, - // rather than the data following the signature. - long locatedZip64EndOfCentralDirLocator = LocateBlockWithSignature( - ZipConstants.Zip64CentralDirLocatorSignature, - locatedEndOfCentralDir - 4, - ZipConstants.Zip64EndOfCentralDirectoryLocatorSize, - 0); - - if (locatedZip64EndOfCentralDirLocator < 0) - { - if (requireZip64) - { - // This is only an error in cases where the Zip64 directory is required. - throw new ZipException("Cannot find Zip64 locator"); - } - } - else - { - isZip64 = true; - - // 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 - ReadLEUint(); // startDisk64 is not currently used - ulong offset64 = ReadLEUlong(); - uint totalDisks = ReadLEUint(); - - baseStream_.Position = (long)offset64; - long sig64 = ReadLEUint(); - - if (sig64 != ZipConstants.Zip64CentralFileHeaderSignature) - { - throw new ZipException(string.Format("Invalid Zip64 Central directory signature at {0:X}", offset64)); - } - - // NOTE: Record size = SizeOfFixedFields + SizeOfVariableData - 12. - ulong recordSize = ReadLEUlong(); - int versionMadeBy = ReadLEUshort(); - int versionToExtract = ReadLEUshort(); - uint thisDisk = ReadLEUint(); - uint centralDirDisk = ReadLEUint(); - entriesForThisDisk = ReadLEUlong(); - entriesForWholeCentralDir = ReadLEUlong(); - centralDirSize = ReadLEUlong(); - offsetOfCentralDir = (long)ReadLEUlong(); - - // NOTE: zip64 extensible data sector (variable size) is ignored. - } - - entries_ = new ZipEntry[entriesForThisDisk]; - - // SFX/embedded support, find the offset of the first entry vis the start of the stream - // This applies to Zip files that are appended to the end of an SFX stub. - // Or are appended as a resource to an executable. - // Zip files created by some archivers have the offsets altered to reflect the true offsets - // and so dont require any adjustment here... - // TODO: Difficulty with Zip64 and SFX offset handling needs resolution - maths? - if (!isZip64 && (offsetOfCentralDir < locatedEndOfCentralDir - (4 + (long)centralDirSize))) - { - offsetOfFirstEntry = locatedEndOfCentralDir - (4 + (long)centralDirSize + offsetOfCentralDir); - if (offsetOfFirstEntry <= 0) - { - throw new ZipException("Invalid embedded zip archive"); - } - } - - baseStream_.Seek(offsetOfFirstEntry + offsetOfCentralDir, SeekOrigin.Begin); - - for (ulong i = 0; i < entriesForThisDisk; i++) - { - if (ReadLEUint() != ZipConstants.CentralHeaderSignature) - { - throw new ZipException("Wrong Central Directory signature"); - } - - int versionMadeBy = ReadLEUshort(); - int versionToExtract = ReadLEUshort(); - int bitFlags = ReadLEUshort(); - int method = ReadLEUshort(); - uint dostime = ReadLEUint(); - uint crc = ReadLEUint(); - var csize = (long)ReadLEUint(); - var size = (long)ReadLEUint(); - int nameLen = ReadLEUshort(); - int extraLen = ReadLEUshort(); - int commentLen = ReadLEUshort(); - - int diskStartNo = ReadLEUshort(); // Not currently used - int internalAttributes = ReadLEUshort(); // Not currently used - - uint externalAttributes = ReadLEUint(); - long offset = ReadLEUint(); - - byte[] buffer = new byte[Math.Max(nameLen, commentLen)]; - var entryEncoding = _stringCodec.ZipInputEncoding(bitFlags); - - StreamUtils.ReadFully(baseStream_, buffer, 0, nameLen); - string name = entryEncoding.GetString(buffer, 0, nameLen); - var unicode = entryEncoding.IsZipUnicode(); - - var entry = new ZipEntry(name, versionToExtract, versionMadeBy, (CompressionMethod)method, unicode) - { - Crc = crc & 0xffffffffL, - Size = size & 0xffffffffL, - CompressedSize = csize & 0xffffffffL, - Flags = bitFlags, - DosTime = dostime, - ZipFileIndex = (long)i, - Offset = offset, - ExternalFileAttributes = (int)externalAttributes - }; - - if ((bitFlags & 8) == 0) - { - entry.CryptoCheckValue = (byte)(crc >> 24); - } - else - { - entry.CryptoCheckValue = (byte)((dostime >> 8) & 0xff); - } - - if (extraLen > 0) - { - byte[] extra = new byte[extraLen]; - StreamUtils.ReadFully(baseStream_, extra); - entry.ExtraData = extra; - } - - entry.ProcessExtraData(false); - - if (commentLen > 0) - { - StreamUtils.ReadFully(baseStream_, buffer, 0, commentLen); - entry.Comment = entryEncoding.GetString(buffer, 0, commentLen); - } - - entries_[i] = entry; - } - } - - /// - /// Locate the data for a given entry. - /// - /// - /// The start offset of the data. - /// - /// - /// The stream ends prematurely - /// - /// - /// The local header signature is invalid, the entry and central header file name lengths are different - /// or the local and entry compression methods dont match - /// - private long LocateEntry(ZipEntry entry) - { - return TestLocalHeader(entry, HeaderTest.Extract); - } - - private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry) - { - CryptoStream result = null; - - if (entry.CompressionMethodForHeader == CompressionMethod.WinZipAES) - { - if (entry.Version >= ZipConstants.VERSION_AES) - { - // Issue #471 - accept an empty string as a password, but reject null. - OnKeysRequired(entry.Name); - if (rawPassword_ == null) - { - throw new ZipException("No password available for AES encrypted stream"); - } - int saltLen = entry.AESSaltLen; - byte[] saltBytes = new byte[saltLen]; - int saltIn = StreamUtils.ReadRequestedBytes(baseStream, saltBytes, 0, saltLen); - if (saltIn != saltLen) - throw new ZipException("AES Salt expected " + saltLen + " got " + saltIn); - // - byte[] pwdVerifyRead = new byte[2]; - StreamUtils.ReadFully(baseStream, pwdVerifyRead); - int blockSize = entry.AESKeySize / 8; // bits to bytes - - var decryptor = new ZipAESTransform(rawPassword_, saltBytes, blockSize, false); - byte[] pwdVerifyCalc = decryptor.PwdVerifier; - if (pwdVerifyCalc[0] != pwdVerifyRead[0] || pwdVerifyCalc[1] != pwdVerifyRead[1]) - throw new ZipException("Invalid password for AES"); - result = new ZipAESStream(baseStream, decryptor, CryptoStreamMode.Read); - } - else - { - throw new ZipException("Decryption method not supported"); - } - } - else - { - if ((entry.Version < ZipConstants.VersionStrongEncryption) - || (entry.Flags & (int)GeneralBitFlags.StrongEncryption) == 0) - { - var classicManaged = new PkzipClassicManaged(); - - OnKeysRequired(entry.Name); - if (HaveKeys == false) - { - throw new ZipException("No password available for encrypted stream"); - } - - result = new CryptoStream(baseStream, classicManaged.CreateDecryptor(key, null), CryptoStreamMode.Read); - CheckClassicPassword(result, entry); - } - else - { - // We don't support PKWare strong encryption - throw new ZipException("Decryption method not supported"); - } - } - - return result; - } - - private Stream CreateAndInitEncryptionStream(Stream baseStream, ZipEntry entry) - { - CryptoStream result = null; - if ((entry.Version < ZipConstants.VersionStrongEncryption) - || (entry.Flags & (int)GeneralBitFlags.StrongEncryption) == 0) - { - var classicManaged = new PkzipClassicManaged(); - - OnKeysRequired(entry.Name); - if (HaveKeys == false) - { - throw new ZipException("No password available for encrypted stream"); - } - - // Closing a CryptoStream will close the base stream as well so wrap it in an UncompressedStream - // which doesnt do this. - result = new CryptoStream(new UncompressedStream(baseStream), - classicManaged.CreateEncryptor(key, null), CryptoStreamMode.Write); - - if ((entry.Crc < 0) || (entry.Flags & 8) != 0) - { - WriteEncryptionHeader(result, entry.DosTime << 16); - } - else - { - WriteEncryptionHeader(result, entry.Crc); - } - } - return result; - } - - private static void CheckClassicPassword(CryptoStream classicCryptoStream, ZipEntry entry) - { - byte[] cryptbuffer = new byte[ZipConstants.CryptoHeaderSize]; - StreamUtils.ReadFully(classicCryptoStream, cryptbuffer); - if (cryptbuffer[ZipConstants.CryptoHeaderSize - 1] != entry.CryptoCheckValue) - { - throw new ZipException("Invalid password"); - } - } - - private static void WriteEncryptionHeader(Stream stream, long crcValue) - { - byte[] cryptBuffer = new byte[ZipConstants.CryptoHeaderSize]; - using (var rng = new RNGCryptoServiceProvider()) - { - rng.GetBytes(cryptBuffer); - } - cryptBuffer[11] = (byte)(crcValue >> 24); - stream.Write(cryptBuffer, 0, cryptBuffer.Length); - } - - #endregion Internal routines - - #region Instance Fields - - private bool isDisposed_; - private string name_; - private string comment_ = string.Empty; - private string rawPassword_; - private Stream baseStream_; - private bool isStreamOwner; - private long offsetOfFirstEntry; - private ZipEntry[] entries_; - private byte[] key; - private bool isNewArchive_; - private StringCodec _stringCodec = ZipStrings.GetStringCodec(); - - // Default is dynamic which is not backwards compatible and can cause problems - // with XP's built in compression which cant read Zip64 archives. - // However it does avoid the situation were a large file is added and cannot be completed correctly. - // Hint: Set always ZipEntry size before they are added to an archive and this setting isnt needed. - private UseZip64 useZip64_ = UseZip64.Dynamic; - - #region Zip Update Instance Fields - - private List updates_; - private long updateCount_; // Count is managed manually as updates_ can contain nulls! - private Dictionary updateIndex_; - private IArchiveStorage archiveStorage_; - private IDynamicDataSource updateDataSource_; - private bool contentsEdited_; - private int bufferSize_ = DefaultBufferSize; - private byte[] copyBuffer_; - private ZipString newComment_; - private bool commentEdited_; - private IEntryFactory updateEntryFactory_ = new ZipEntryFactory(); - - #endregion Zip Update Instance Fields - - #endregion Instance Fields - - #region Support Classes - - /// - /// Represents a string from a which is stored as an array of bytes. - /// - private class ZipString - { - #region Constructors - - /// - /// Initialise a with a string. - /// - /// The textual string form. - /// - public ZipString(string comment, Encoding encoding) - { - comment_ = comment; - isSourceString_ = true; - _encoding = encoding; - } - - /// - /// Initialise a using a string in its binary 'raw' form. - /// - /// - /// - public ZipString(byte[] rawString, Encoding encoding) - { - rawComment_ = rawString; - _encoding = encoding; - } - - #endregion Constructors - - /// - /// Get a value indicating the original source of data for this instance. - /// True if the source was a string; false if the source was binary data. - /// - public bool IsSourceString => isSourceString_; - - /// - /// Get the length of the comment when represented as raw bytes. - /// - public int RawLength - { - get - { - MakeBytesAvailable(); - return rawComment_.Length; - } - } - - /// - /// Get the comment in its 'raw' form as plain bytes. - /// - public byte[] RawComment - { - get - { - MakeBytesAvailable(); - return (byte[])rawComment_.Clone(); - } - } - - /// - /// Reset the comment to its initial state. - /// - public void Reset() - { - if (isSourceString_) - { - rawComment_ = null; - } - else - { - comment_ = null; - } - } - - private void MakeTextAvailable() - { - if (comment_ == null) - { - comment_ = _encoding.GetString(rawComment_); - } - } - - private void MakeBytesAvailable() - { - if (rawComment_ == null) - { - rawComment_ = _encoding.GetBytes(comment_); - } - } - - /// - /// Implicit conversion of comment to a string. - /// - /// The to convert to a string. - /// The textual equivalent for the input value. - public static implicit operator string(ZipString zipString) - { - zipString.MakeTextAvailable(); - return zipString.comment_; - } - - #region Instance Fields - - private string comment_; - private byte[] rawComment_; - private readonly bool isSourceString_; - private readonly Encoding _encoding; - - #endregion Instance Fields - } - - /// - /// An enumerator for Zip entries - /// - private class ZipEntryEnumerator : IEnumerator - { - #region Constructors - - public ZipEntryEnumerator(ZipEntry[] entries) - { - array = entries; - } - - #endregion Constructors - - #region IEnumerator Members - - public object Current - { - get - { - return array[index]; - } - } - - public void Reset() - { - index = -1; - } - - public bool MoveNext() - { - return (++index < array.Length); - } - - #endregion IEnumerator Members - - #region Instance Fields - - private ZipEntry[] array; - private int index = -1; - - #endregion Instance Fields - } - - /// - /// An is a stream that you can write uncompressed data - /// to and flush, but cannot read, seek or do anything else to. - /// - private class UncompressedStream : Stream - { - #region Constructors - - public UncompressedStream(Stream baseStream) - { - baseStream_ = baseStream; - } - - #endregion Constructors - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - public override bool CanRead - { - get - { - return false; - } - } - - /// - /// Write any buffered data to underlying storage. - /// - public override void Flush() - { - baseStream_.Flush(); - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - public override bool CanWrite - { - get - { - return baseStream_.CanWrite; - } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - public override bool CanSeek - { - get - { - return false; - } - } - - /// - /// Get the length in bytes of the stream. - /// - public override long Length - { - get - { - return 0; - } - } - - /// - /// Gets or sets the position within the current stream. - /// - public override long Position - { - get - { - return baseStream_.Position; - } - set - { - throw new NotImplementedException(); - } - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// - /// The sum of offset and count is larger than the buffer length. - /// Methods were called after the stream was closed. - /// The stream does not support reading. - /// buffer is null. - /// An I/O error occurs. - /// offset or count is negative. - public override int Read(byte[] buffer, int offset, int count) - { - return 0; - } - - /// - /// Sets the position within the current stream. - /// - /// A byte offset relative to the origin parameter. - /// A value of type indicating the reference point used to obtain the new position. - /// - /// The new position within the current stream. - /// - /// An I/O error occurs. - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. - public override long Seek(long offset, SeekOrigin origin) - { - return 0; - } - - /// - /// Sets the length of the current stream. - /// - /// The desired length of the current stream in bytes. - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// An I/O error occurs. - /// Methods were called after the stream was closed. - public override void SetLength(long value) - { - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// An I/O error occurs. - /// The stream does not support writing. - /// Methods were called after the stream was closed. - /// buffer is null. - /// The sum of offset and count is greater than the buffer length. - /// offset or count is negative. - public override void Write(byte[] buffer, int offset, int count) - { - baseStream_.Write(buffer, offset, count); - } - - private readonly - - #region Instance Fields - - Stream baseStream_; - - #endregion Instance Fields - } - - /// - /// A is an - /// whose data is only a part or subsection of a file. - /// - private class PartialInputStream : Stream - { - #region Constructors - - /// - /// Initialise a new instance of the class. - /// - /// The containing the underlying stream to use for IO. - /// The start of the partial data. - /// The length of the partial data. - public PartialInputStream(ZipFile zipFile, long start, long length) - { - start_ = start; - length_ = length; - - // Although this is the only time the zipfile is used - // keeping a reference here prevents premature closure of - // this zip file and thus the baseStream_. - - // Code like this will cause apparently random failures depending - // on the size of the files and when garbage is collected. - // - // ZipFile z = new ZipFile (stream); - // Stream reader = z.GetInputStream(0); - // uses reader here.... - zipFile_ = zipFile; - baseStream_ = zipFile_.baseStream_; - readPos_ = start; - end_ = start + length; - } - - #endregion Constructors - - /// - /// Read a byte from this stream. - /// - /// Returns the byte read or -1 on end of stream. - public override int ReadByte() - { - if (readPos_ >= end_) - { - // -1 is the correct value at end of stream. - return -1; - } - - lock (baseStream_) - { - baseStream_.Seek(readPos_++, SeekOrigin.Begin); - return baseStream_.ReadByte(); - } - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// - /// The sum of offset and count is larger than the buffer length. - /// Methods were called after the stream was closed. - /// The stream does not support reading. - /// buffer is null. - /// An I/O error occurs. - /// offset or count is negative. - public override int Read(byte[] buffer, int offset, int count) - { - lock (baseStream_) - { - if (count > end_ - readPos_) - { - count = (int)(end_ - readPos_); - if (count == 0) - { - return 0; - } - } - // Protect against Stream implementations that throw away their buffer on every Seek - // (for example, Mono FileStream) - if (baseStream_.Position != readPos_) - { - baseStream_.Seek(readPos_, SeekOrigin.Begin); - } - int readCount = baseStream_.Read(buffer, offset, count); - if (readCount > 0) - { - readPos_ += readCount; - } - return readCount; - } - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// An I/O error occurs. - /// The stream does not support writing. - /// Methods were called after the stream was closed. - /// buffer is null. - /// The sum of offset and count is greater than the buffer length. - /// offset or count is negative. - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - - /// - /// When overridden in a derived class, sets the length of the current stream. - /// - /// The desired length of the current stream in bytes. - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// An I/O error occurs. - /// Methods were called after the stream was closed. - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - /// - /// When overridden in a derived class, sets the position within the current stream. - /// - /// A byte offset relative to the origin parameter. - /// A value of type indicating the reference point used to obtain the new position. - /// - /// The new position within the current stream. - /// - /// An I/O error occurs. - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. - public override long Seek(long offset, SeekOrigin origin) - { - long newPos = readPos_; - - switch (origin) - { - case SeekOrigin.Begin: - newPos = start_ + offset; - break; - - case SeekOrigin.Current: - newPos = readPos_ + offset; - break; - - case SeekOrigin.End: - newPos = end_ + offset; - break; - } - - if (newPos < start_) - { - throw new ArgumentException("Negative position is invalid"); - } - - if (newPos > end_) - { - throw new IOException("Cannot seek past end"); - } - readPos_ = newPos; - return readPos_; - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - /// An I/O error occurs. - public override void Flush() - { - // Nothing to do. - } - - /// - /// Gets or sets the position within the current stream. - /// - /// - /// The current position within the stream. - /// An I/O error occurs. - /// The stream does not support seeking. - /// Methods were called after the stream was closed. - public override long Position - { - get { return readPos_ - start_; } - set - { - long newPos = start_ + value; - - if (newPos < start_) - { - throw new ArgumentException("Negative position is invalid"); - } - - if (newPos > end_) - { - throw new InvalidOperationException("Cannot seek past end"); - } - readPos_ = newPos; - } - } - - /// - /// Gets the length in bytes of the stream. - /// - /// - /// A long value representing the length of the stream in bytes. - /// A class derived from Stream does not support seeking. - /// Methods were called after the stream was closed. - public override long Length - { - get { return length_; } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// false - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite - { - get { return false; } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// true - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek - { - get { return true; } - } - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// true. - /// true if the stream supports reading; otherwise, false. - public override bool CanRead - { - get { return true; } - } - - /// - /// Gets a value that determines whether the current stream can time out. - /// - /// - /// A value that determines whether the current stream can time out. - public override bool CanTimeout - { - get { return baseStream_.CanTimeout; } - } - - #region Instance Fields - - private ZipFile zipFile_; - private Stream baseStream_; - private readonly long start_; - private readonly long length_; - private long readPos_; - private readonly long end_; - - #endregion Instance Fields - } - - #endregion Support Classes - } - - #endregion ZipFile Class - - #region DataSources - - /// - /// Provides a static way to obtain a source of data for an entry. - /// - public interface IStaticDataSource - { - /// - /// Get a source of data by creating a new stream. - /// - /// Returns a to use for compression input. - /// Ideally a new stream is created and opened to achieve this, to avoid locking problems. - Stream GetSource(); - } - - /// - /// Represents a source of data that can dynamically provide - /// multiple data sources based on the parameters passed. - /// - public interface IDynamicDataSource - { - /// - /// Get a data source. - /// - /// The to get a source for. - /// The name for data if known. - /// Returns a to use for compression input. - /// Ideally a new stream is created and opened to achieve this, to avoid locking problems. - Stream GetSource(ZipEntry entry, string name); - } - - /// - /// Default implementation of a for use with files stored on disk. - /// - public class StaticDiskDataSource : IStaticDataSource - { - /// - /// Initialise a new instance of - /// - /// The name of the file to obtain data from. - public StaticDiskDataSource(string fileName) - { - fileName_ = fileName; - } - - #region IDataSource Members - - /// - /// Get a providing data. - /// - /// Returns a providing data. - public Stream GetSource() - { - return File.Open(fileName_, FileMode.Open, FileAccess.Read, FileShare.Read); - } - - private readonly - - #endregion IDataSource Members - - #region Instance Fields - - string fileName_; - - #endregion Instance Fields - } - - /// - /// Default implementation of for files stored on disk. - /// - public class DynamicDiskDataSource : IDynamicDataSource - { - #region IDataSource Members - - /// - /// Get a providing data for an entry. - /// - /// The entry to provide data for. - /// The file name for data if known. - /// Returns a stream providing data; or null if not available - public Stream GetSource(ZipEntry entry, string name) - { - Stream result = null; - - if (name != null) - { - result = File.Open(name, FileMode.Open, FileAccess.Read, FileShare.Read); - } - - return result; - } - - #endregion IDataSource Members - } - - #endregion DataSources - - #region Archive Storage - - /// - /// Defines facilities for data storage when updating Zip Archives. - /// - public interface IArchiveStorage - { - /// - /// Get the to apply during updates. - /// - FileUpdateMode UpdateMode { get; } - - /// - /// Get an empty that can be used for temporary output. - /// - /// Returns a temporary output - /// - Stream GetTemporaryOutput(); - - /// - /// Convert a temporary output stream to a final stream. - /// - /// The resulting final - /// - Stream ConvertTemporaryToFinal(); - - /// - /// Make a temporary copy of the original stream. - /// - /// The to copy. - /// Returns a temporary output that is a copy of the input. - Stream MakeTemporaryCopy(Stream stream); - - /// - /// Return a stream suitable for performing direct updates on the original source. - /// - /// The current stream. - /// Returns a stream suitable for direct updating. - /// This may be the current stream passed. - Stream OpenForDirectUpdate(Stream stream); - - /// - /// Dispose of this instance. - /// - void Dispose(); - } - - /// - /// An abstract suitable for extension by inheritance. - /// - abstract public class BaseArchiveStorage : IArchiveStorage - { - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The update mode. - protected BaseArchiveStorage(FileUpdateMode updateMode) - { - updateMode_ = updateMode; - } - - #endregion Constructors - - #region IArchiveStorage Members - - /// - /// Gets a temporary output - /// - /// Returns the temporary output stream. - /// - public abstract Stream GetTemporaryOutput(); - - /// - /// Converts the temporary to its final form. - /// - /// Returns a that can be used to read - /// the final storage for the archive. - /// - public abstract Stream ConvertTemporaryToFinal(); - - /// - /// Make a temporary copy of a . - /// - /// The to make a copy of. - /// Returns a temporary output that is a copy of the input. - public abstract Stream MakeTemporaryCopy(Stream stream); - - /// - /// Return a stream suitable for performing direct updates on the original source. - /// - /// The to open for direct update. - /// Returns a stream suitable for direct updating. - public abstract Stream OpenForDirectUpdate(Stream stream); - - /// - /// Disposes this instance. - /// - public abstract void Dispose(); - - /// - /// Gets the update mode applicable. - /// - /// The update mode. - public FileUpdateMode UpdateMode - { - get - { - return updateMode_; - } - } - - #endregion IArchiveStorage Members - - #region Instance Fields - - private readonly FileUpdateMode updateMode_; - - #endregion Instance Fields - } - - /// - /// An implementation suitable for hard disks. - /// - public class DiskArchiveStorage : BaseArchiveStorage - { - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The file. - /// The update mode. - public DiskArchiveStorage(ZipFile file, FileUpdateMode updateMode) - : base(updateMode) - { - if (file.Name == null) - { - throw new ZipException("Cant handle non file archives"); - } - - fileName_ = file.Name; - } - - /// - /// Initializes a new instance of the class. - /// - /// The file. - public DiskArchiveStorage(ZipFile file) - : this(file, FileUpdateMode.Safe) - { - } - - #endregion Constructors - - #region IArchiveStorage Members - - /// - /// Gets a temporary output for performing updates on. - /// - /// Returns the temporary output stream. - public override Stream GetTemporaryOutput() - { - temporaryName_ = PathUtils.GetTempFileName(temporaryName_); - temporaryStream_ = File.Open(temporaryName_, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); - - return temporaryStream_; - } - - /// - /// Converts a temporary to its final form. - /// - /// Returns a that can be used to read - /// the final storage for the archive. - public override Stream ConvertTemporaryToFinal() - { - if (temporaryStream_ == null) - { - throw new ZipException("No temporary stream has been created"); - } - - Stream result = null; - - string moveTempName = PathUtils.GetTempFileName(fileName_); - bool newFileCreated = false; - - try - { - temporaryStream_.Dispose(); - File.Move(fileName_, moveTempName); - File.Move(temporaryName_, fileName_); - newFileCreated = true; - File.Delete(moveTempName); - - result = File.Open(fileName_, FileMode.Open, FileAccess.Read, FileShare.Read); - } - catch (Exception) - { - result = null; - - // Try to roll back changes... - if (!newFileCreated) - { - File.Move(moveTempName, fileName_); - File.Delete(temporaryName_); - } - - throw; - } - - return result; - } - - /// - /// Make a temporary copy of a stream. - /// - /// The to copy. - /// Returns a temporary output that is a copy of the input. - public override Stream MakeTemporaryCopy(Stream stream) - { - stream.Dispose(); - - temporaryName_ = PathUtils.GetTempFileName(fileName_); - File.Copy(fileName_, temporaryName_, true); - - temporaryStream_ = new FileStream(temporaryName_, - FileMode.Open, - FileAccess.ReadWrite); - return temporaryStream_; - } - - /// - /// Return a stream suitable for performing direct updates on the original source. - /// - /// The current stream. - /// Returns a stream suitable for direct updating. - /// If the is not null this is used as is. - public override Stream OpenForDirectUpdate(Stream stream) - { - Stream result; - if ((stream == null) || !stream.CanWrite) - { - if (stream != null) - { - stream.Dispose(); - } - - result = new FileStream(fileName_, - FileMode.Open, - FileAccess.ReadWrite); - } - else - { - result = stream; - } - - return result; - } - - /// - /// Disposes this instance. - /// - public override void Dispose() - { - if (temporaryStream_ != null) - { - temporaryStream_.Dispose(); - } - } - - #endregion IArchiveStorage Members - - #region Instance Fields - - private Stream temporaryStream_; - private readonly string fileName_; - private string temporaryName_; - - #endregion Instance Fields - } - - /// - /// An implementation suitable for in memory streams. - /// - public class MemoryArchiveStorage : BaseArchiveStorage - { - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - public MemoryArchiveStorage() - : base(FileUpdateMode.Direct) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The to use - /// This constructor is for testing as memory streams dont really require safe mode. - public MemoryArchiveStorage(FileUpdateMode updateMode) - : base(updateMode) - { - } - - #endregion Constructors - - #region Properties - - /// - /// Get the stream returned by if this was in fact called. - /// - public MemoryStream FinalStream - { - get { return finalStream_; } - } - - #endregion Properties - - #region IArchiveStorage Members - - /// - /// Gets the temporary output - /// - /// Returns the temporary output stream. - public override Stream GetTemporaryOutput() - { - temporaryStream_ = new MemoryStream(); - return temporaryStream_; - } - - /// - /// Converts the temporary to its final form. - /// - /// Returns a that can be used to read - /// the final storage for the archive. - public override Stream ConvertTemporaryToFinal() - { - if (temporaryStream_ == null) - { - throw new ZipException("No temporary stream has been created"); - } - - finalStream_ = new MemoryStream(temporaryStream_.ToArray()); - return finalStream_; - } - - /// - /// Make a temporary copy of the original stream. - /// - /// The to copy. - /// Returns a temporary output that is a copy of the input. - public override Stream MakeTemporaryCopy(Stream stream) - { - temporaryStream_ = new MemoryStream(); - stream.Position = 0; - StreamUtils.Copy(stream, temporaryStream_, new byte[4096]); - return temporaryStream_; - } - - /// - /// Return a stream suitable for performing direct updates on the original source. - /// - /// The original source stream - /// Returns a stream suitable for direct updating. - /// If the passed is not null this is used; - /// otherwise a new is returned. - public override Stream OpenForDirectUpdate(Stream stream) - { - Stream result; - if ((stream == null) || !stream.CanWrite) - { - result = new MemoryStream(); - - if (stream != null) - { - stream.Position = 0; - StreamUtils.Copy(stream, result, new byte[4096]); - - stream.Dispose(); - } - } - else - { - result = stream; - } - - return result; - } - - /// - /// Disposes this instance. - /// - public override void Dispose() - { - if (temporaryStream_ != null) - { - temporaryStream_.Dispose(); - } - } - - #endregion IArchiveStorage Members - - #region Instance Fields - - private MemoryStream temporaryStream_; - private MemoryStream finalStream_; - - #endregion Instance Fields - } - - #endregion Archive Storage -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs b/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs deleted file mode 100644 index a37ab3031438..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs +++ /dev/null @@ -1,597 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using ICSharpCode.SharpZipLib.Core; - -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// Holds data pertinent to a data descriptor. - /// - public class DescriptorData - { - private long _crc; - - /// - /// Get /set the compressed size of data. - /// - public long CompressedSize { get; set; } - - /// - /// Get / set the uncompressed size of data - /// - public long Size { get; set; } - - /// - /// Get /set the crc value. - /// - public long Crc - { - get => _crc; - set => _crc = (value & 0xffffffff); - } - } - - internal struct EntryPatchData - { - public long SizePatchOffset { get; set; } - - public long CrcPatchOffset { get; set; } - } - - /// - /// This class assists with writing/reading from Zip files. - /// - internal static class ZipFormat - { - // Write the local file header - // TODO: ZipFormat.WriteLocalHeader is not yet used and needs checking for ZipFile and ZipOuptutStream usage - internal static int WriteLocalHeader(Stream stream, ZipEntry entry, out EntryPatchData patchData, - bool headerInfoAvailable, bool patchEntryHeader, long streamOffset, StringCodec stringCodec) - { - patchData = new EntryPatchData(); - - stream.WriteLEInt(ZipConstants.LocalHeaderSignature); - stream.WriteLEShort(entry.Version); - stream.WriteLEShort(entry.Flags); - stream.WriteLEShort((byte)entry.CompressionMethodForHeader); - stream.WriteLEInt((int)entry.DosTime); - - if (headerInfoAvailable) - { - stream.WriteLEInt((int)entry.Crc); - if (entry.LocalHeaderRequiresZip64) - { - stream.WriteLEInt(-1); - stream.WriteLEInt(-1); - } - else - { - stream.WriteLEInt((int)entry.CompressedSize + entry.EncryptionOverheadSize); - stream.WriteLEInt((int)entry.Size); - } - } - else - { - if (patchEntryHeader) - patchData.CrcPatchOffset = streamOffset + stream.Position; - - stream.WriteLEInt(0); // Crc - - if (patchEntryHeader) - patchData.SizePatchOffset = streamOffset + stream.Position; - - // For local header both sizes appear in Zip64 Extended Information - if (entry.LocalHeaderRequiresZip64 && patchEntryHeader) - { - stream.WriteLEInt(-1); - stream.WriteLEInt(-1); - } - else - { - stream.WriteLEInt(0); // Compressed size - stream.WriteLEInt(0); // Uncompressed size - } - } - - byte[] name = stringCodec.ZipOutputEncoding.GetBytes(entry.Name); - - if (name.Length > 0xFFFF) - { - throw new ZipException("Entry name too long."); - } - - var ed = new ZipExtraData(entry.ExtraData); - - if (entry.LocalHeaderRequiresZip64) - { - ed.StartNewEntry(); - if (headerInfoAvailable) - { - ed.AddLeLong(entry.Size); - ed.AddLeLong(entry.CompressedSize + entry.EncryptionOverheadSize); - } - else - { - ed.AddLeLong(-1); - ed.AddLeLong(-1); - } - ed.AddNewEntry(1); - - if (!ed.Find(1)) - { - throw new ZipException("Internal error cant find extra data"); - } - - patchData.SizePatchOffset = ed.CurrentReadIndex; - } - else - { - ed.Delete(1); - } - - if (entry.AESKeySize > 0) - { - AddExtraDataAES(entry, ed); - } - byte[] extra = ed.GetEntryData(); - - stream.WriteLEShort(name.Length); - stream.WriteLEShort(extra.Length); - - if (name.Length > 0) - { - stream.Write(name, 0, name.Length); - } - - if (entry.LocalHeaderRequiresZip64 && patchEntryHeader) - { - patchData.SizePatchOffset += streamOffset + stream.Position; - } - - if (extra.Length > 0) - { - stream.Write(extra, 0, extra.Length); - } - - return ZipConstants.LocalHeaderBaseSize + name.Length + extra.Length; - } - - /// - /// Locates a block with the desired . - /// - /// - /// The signature to find. - /// Location, marking the end of block. - /// Minimum size of the block. - /// The maximum variable data. - /// Returns the offset of the first byte after the signature; -1 if not found - internal static long LocateBlockWithSignature(Stream stream, int signature, long endLocation, int minimumBlockSize, int maximumVariableData) - { - long pos = endLocation - minimumBlockSize; - if (pos < 0) - { - return -1; - } - - long giveUpMarker = Math.Max(pos - maximumVariableData, 0); - - // TODO: This loop could be optimized for speed. - do - { - if (pos < giveUpMarker) - { - return -1; - } - stream.Seek(pos--, SeekOrigin.Begin); - } while (stream.ReadLEInt() != signature); - - return stream.Position; - } - - /// - public static async Task WriteZip64EndOfCentralDirectoryAsync(Stream stream, long noOfEntries, - long sizeEntries, long centralDirOffset, CancellationToken cancellationToken) - { - await stream.WriteProcToStreamAsync(s => WriteZip64EndOfCentralDirectory(s, noOfEntries, sizeEntries, centralDirOffset), cancellationToken); - } - - /// - /// Write Zip64 end of central directory records (File header and locator). - /// - /// - /// The number of entries in the central directory. - /// The size of entries in the central directory. - /// The offset of the central directory. - internal static void WriteZip64EndOfCentralDirectory(Stream stream, long noOfEntries, long sizeEntries, long centralDirOffset) - { - long centralSignatureOffset = centralDirOffset + sizeEntries; - stream.WriteLEInt(ZipConstants.Zip64CentralFileHeaderSignature); - stream.WriteLELong(44); // Size of this record (total size of remaining fields in header or full size - 12) - stream.WriteLEShort(ZipConstants.VersionMadeBy); // Version made by - stream.WriteLEShort(ZipConstants.VersionZip64); // Version to extract - stream.WriteLEInt(0); // Number of this disk - stream.WriteLEInt(0); // number of the disk with the start of the central directory - stream.WriteLELong(noOfEntries); // No of entries on this disk - stream.WriteLELong(noOfEntries); // Total No of entries in central directory - stream.WriteLELong(sizeEntries); // Size of the central directory - stream.WriteLELong(centralDirOffset); // offset of start of central directory - // zip64 extensible data sector not catered for here (variable size) - - // Write the Zip64 end of central directory locator - stream.WriteLEInt(ZipConstants.Zip64CentralDirLocatorSignature); - - // no of the disk with the start of the zip64 end of central directory - stream.WriteLEInt(0); - - // relative offset of the zip64 end of central directory record - stream.WriteLELong(centralSignatureOffset); - - // total number of disks - stream.WriteLEInt(1); - } - - /// - public static async Task WriteEndOfCentralDirectoryAsync(Stream stream, long noOfEntries, long sizeEntries, - long start, byte[] comment, CancellationToken cancellationToken) - => await stream.WriteProcToStreamAsync(s - => WriteEndOfCentralDirectory(s, noOfEntries, sizeEntries, start, comment), cancellationToken); - - /// - /// Write the required records to end the central directory. - /// - /// - /// The number of entries in the directory. - /// The size of the entries in the directory. - /// The start of the central directory. - /// The archive comment. (This can be null). - - internal static void WriteEndOfCentralDirectory(Stream stream, long noOfEntries, long sizeEntries, long start, byte[] comment) - { - if (noOfEntries >= 0xffff || - start >= 0xffffffff || - sizeEntries >= 0xffffffff) - { - WriteZip64EndOfCentralDirectory(stream, noOfEntries, sizeEntries, start); - } - - stream.WriteLEInt(ZipConstants.EndOfCentralDirectorySignature); - - // TODO: ZipFile Multi disk handling not done - stream.WriteLEShort(0); // number of this disk - stream.WriteLEShort(0); // no of disk with start of central dir - - // Number of entries - if (noOfEntries >= 0xffff) - { - stream.WriteLEUshort(0xffff); // Zip64 marker - stream.WriteLEUshort(0xffff); - } - else - { - stream.WriteLEShort((short)noOfEntries); // entries in central dir for this disk - stream.WriteLEShort((short)noOfEntries); // total entries in central directory - } - - // Size of the central directory - if (sizeEntries >= 0xffffffff) - { - stream.WriteLEUint(0xffffffff); // Zip64 marker - } - else - { - stream.WriteLEInt((int)sizeEntries); - } - - // offset of start of central directory - if (start >= 0xffffffff) - { - stream.WriteLEUint(0xffffffff); // Zip64 marker - } - else - { - stream.WriteLEInt((int)start); - } - - var commentLength = comment?.Length ?? 0; - - if (commentLength > 0xffff) - { - throw new ZipException($"Comment length ({commentLength}) is larger than 64K"); - } - - stream.WriteLEShort(commentLength); - - if (commentLength > 0) - { - stream.Write(comment, 0, commentLength); - } - } - - - - /// - /// Write a data descriptor. - /// - /// - /// The entry to write a descriptor for. - /// Returns the number of descriptor bytes written. - internal static int WriteDataDescriptor(Stream stream, ZipEntry entry) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - int result = 0; - - // Add data descriptor if flagged as required - if ((entry.Flags & (int)GeneralBitFlags.Descriptor) != 0) - { - // The signature is not PKZIP originally but is now described as optional - // in the PKZIP Appnote documenting the format. - stream.WriteLEInt(ZipConstants.DataDescriptorSignature); - stream.WriteLEInt(unchecked((int)(entry.Crc))); - - result += 8; - - if (entry.LocalHeaderRequiresZip64) - { - stream.WriteLELong(entry.CompressedSize); - stream.WriteLELong(entry.Size); - result += 16; - } - else - { - stream.WriteLEInt((int)entry.CompressedSize); - stream.WriteLEInt((int)entry.Size); - result += 8; - } - } - - return result; - } - - /// - /// Read data descriptor at the end of compressed data. - /// - /// - /// if set to true [zip64]. - /// The data to fill in. - /// Returns the number of bytes read in the descriptor. - internal static void ReadDataDescriptor(Stream stream, bool zip64, DescriptorData data) - { - int intValue = stream.ReadLEInt(); - - // In theory this may not be a descriptor according to PKZIP appnote. - // In practice its always there. - if (intValue != ZipConstants.DataDescriptorSignature) - { - throw new ZipException("Data descriptor signature not found"); - } - - data.Crc = stream.ReadLEInt(); - - if (zip64) - { - data.CompressedSize = stream.ReadLELong(); - data.Size = stream.ReadLELong(); - } - else - { - data.CompressedSize = stream.ReadLEInt(); - data.Size = stream.ReadLEInt(); - } - } - - internal static int WriteEndEntry(Stream stream, ZipEntry entry, StringCodec stringCodec) - { - stream.WriteLEInt(ZipConstants.CentralHeaderSignature); - stream.WriteLEShort((entry.HostSystem << 8) | entry.VersionMadeBy); - stream.WriteLEShort(entry.Version); - stream.WriteLEShort(entry.Flags); - stream.WriteLEShort((short)entry.CompressionMethodForHeader); - stream.WriteLEInt((int)entry.DosTime); - stream.WriteLEInt((int)entry.Crc); - - if (entry.IsZip64Forced() || - (entry.CompressedSize >= uint.MaxValue)) - { - stream.WriteLEInt(-1); - } - else - { - stream.WriteLEInt((int)entry.CompressedSize); - } - - if (entry.IsZip64Forced() || - (entry.Size >= uint.MaxValue)) - { - stream.WriteLEInt(-1); - } - else - { - stream.WriteLEInt((int)entry.Size); - } - - byte[] name = stringCodec.ZipOutputEncoding.GetBytes(entry.Name); - - if (name.Length > 0xffff) - { - throw new ZipException("Name too long."); - } - - var ed = new ZipExtraData(entry.ExtraData); - - if (entry.CentralHeaderRequiresZip64) - { - ed.StartNewEntry(); - if (entry.IsZip64Forced() || - (entry.Size >= 0xffffffff)) - { - ed.AddLeLong(entry.Size); - } - - if (entry.IsZip64Forced() || - (entry.CompressedSize >= 0xffffffff)) - { - ed.AddLeLong(entry.CompressedSize); - } - - if (entry.Offset >= 0xffffffff) - { - ed.AddLeLong(entry.Offset); - } - - ed.AddNewEntry(1); - } - else - { - ed.Delete(1); - } - - if (entry.AESKeySize > 0) - { - AddExtraDataAES(entry, ed); - } - byte[] extra = ed.GetEntryData(); - - byte[] entryComment = !(entry.Comment is null) - ? stringCodec.ZipOutputEncoding.GetBytes(entry.Comment) - : Empty.Array(); - - if (entryComment.Length > 0xffff) - { - throw new ZipException("Comment too long."); - } - - stream.WriteLEShort(name.Length); - stream.WriteLEShort(extra.Length); - stream.WriteLEShort(entryComment.Length); - stream.WriteLEShort(0); // disk number - stream.WriteLEShort(0); // internal file attributes - // external file attributes - - if (entry.ExternalFileAttributes != -1) - { - stream.WriteLEInt(entry.ExternalFileAttributes); - } - else - { - if (entry.IsDirectory) - { // mark entry as directory (from nikolam.AT.perfectinfo.com) - stream.WriteLEInt(16); - } - else - { - stream.WriteLEInt(0); - } - } - - if (entry.Offset >= uint.MaxValue) - { - stream.WriteLEInt(-1); - } - else - { - stream.WriteLEInt((int)entry.Offset); - } - - if (name.Length > 0) - { - stream.Write(name, 0, name.Length); - } - - if (extra.Length > 0) - { - stream.Write(extra, 0, extra.Length); - } - - if (entryComment.Length > 0) - { - stream.Write(entryComment, 0, entryComment.Length); - } - - return ZipConstants.CentralHeaderBaseSize + name.Length + extra.Length + entryComment.Length; - } - - internal static void AddExtraDataAES(ZipEntry entry, ZipExtraData extraData) - { - // Vendor Version: AE-1 IS 1. AE-2 is 2. With AE-2 no CRC is required and 0 is stored. - const int VENDOR_VERSION = 2; - // Vendor ID is the two ASCII characters "AE". - const int VENDOR_ID = 0x4541; //not 6965; - extraData.StartNewEntry(); - // Pack AES extra data field see http://www.winzip.com/aes_info.htm - //extraData.AddLeShort(7); // Data size (currently 7) - extraData.AddLeShort(VENDOR_VERSION); // 2 = AE-2 - extraData.AddLeShort(VENDOR_ID); // "AE" - extraData.AddData(entry.AESEncryptionStrength); // 1 = 128, 2 = 192, 3 = 256 - extraData.AddLeShort((int)entry.CompressionMethod); // The actual compression method used to compress the file - extraData.AddNewEntry(0x9901); - } - - internal static async Task PatchLocalHeaderAsync(Stream stream, ZipEntry entry, - EntryPatchData patchData, CancellationToken ct) - { - var initialPos = stream.Position; - - // Update CRC - stream.Seek(patchData.CrcPatchOffset, SeekOrigin.Begin); - await stream.WriteLEIntAsync((int)entry.Crc, ct); - - // Update Sizes - if (entry.LocalHeaderRequiresZip64) - { - if (patchData.SizePatchOffset == -1) - { - throw new ZipException("Entry requires zip64 but this has been turned off"); - } - // Seek to the Zip64 Extra Data - stream.Seek(patchData.SizePatchOffset, SeekOrigin.Begin); - - // Note: The order of the size fields is reversed when compared to the local header! - await stream.WriteLELongAsync(entry.Size, ct); - await stream.WriteLELongAsync(entry.CompressedSize, ct); - } - else - { - await stream.WriteLEIntAsync((int)entry.CompressedSize, ct); - await stream.WriteLEIntAsync((int)entry.Size, ct); - } - - stream.Seek(initialPos, SeekOrigin.Begin); - } - - internal static void PatchLocalHeaderSync(Stream stream, ZipEntry entry, - EntryPatchData patchData) - { - var initialPos = stream.Position; - stream.Seek(patchData.CrcPatchOffset, SeekOrigin.Begin); - stream.WriteLEInt((int)entry.Crc); - - if (entry.LocalHeaderRequiresZip64) - { - if (patchData.SizePatchOffset == -1) - { - throw new ZipException("Entry requires zip64 but this has been turned off"); - } - - // Seek to the Zip64 Extra Data - stream.Seek(patchData.SizePatchOffset, SeekOrigin.Begin); - - // Note: The order of the size fields is reversed when compared to the local header! - stream.WriteLELong(entry.Size); - stream.WriteLELong(entry.CompressedSize); - } - else - { - stream.WriteLEInt((int)entry.CompressedSize); - stream.WriteLEInt((int)entry.Size); - } - - stream.Seek(initialPos, SeekOrigin.Begin); - } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs b/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs b/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs deleted file mode 100644 index 1b5b0ad530c9..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs +++ /dev/null @@ -1,730 +0,0 @@ -using ICSharpCode.SharpZipLib.Checksum; -using ICSharpCode.SharpZipLib.Encryption; -using ICSharpCode.SharpZipLib.Zip.Compression; -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; -using System; -using System.IO; - -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// This is an InflaterInputStream that reads the files baseInputStream an zip archive - /// one after another. It has a special method to get the zip entry of - /// the next file. The zip entry contains information about the file name - /// size, compressed size, Crc, etc. - /// It includes support for Stored and Deflated entries. - ///
- ///
Author of the original java version : Jochen Hoenicke - ///
- /// - /// This sample shows how to read a zip file - /// - /// using System; - /// using System.Text; - /// using System.IO; - /// - /// using ICSharpCode.SharpZipLib.Zip; - /// - /// class MainClass - /// { - /// public static void Main(string[] args) - /// { - /// using ( ZipInputStream s = new ZipInputStream(File.OpenRead(args[0]))) { - /// - /// ZipEntry theEntry; - /// const int size = 2048; - /// byte[] data = new byte[2048]; - /// - /// while ((theEntry = s.GetNextEntry()) != null) { - /// if ( entry.IsFile ) { - /// Console.Write("Show contents (y/n) ?"); - /// if (Console.ReadLine() == "y") { - /// while (true) { - /// size = s.Read(data, 0, data.Length); - /// if (size > 0) { - /// Console.Write(new ASCIIEncoding().GetString(data, 0, size)); - /// } else { - /// break; - /// } - /// } - /// } - /// } - /// } - /// } - /// } - /// } - /// - /// - public class ZipInputStream : InflaterInputStream - { - #region Instance Fields - - /// - /// Delegate for reading bytes from a stream. - /// - private delegate int ReadDataHandler(byte[] b, int offset, int length); - - /// - /// The current reader this instance. - /// - private ReadDataHandler internalReader; - - private Crc32 crc = new Crc32(); - private ZipEntry entry; - - private long size; - private CompressionMethod method; - private int flags; - private string password; - private readonly StringCodec _stringCodec = ZipStrings.GetStringCodec(); - - #endregion Instance Fields - - #region Constructors - - /// - /// Creates a new Zip input stream, for reading a zip archive. - /// - /// The underlying providing data. - public ZipInputStream(Stream baseInputStream) - : base(baseInputStream, new Inflater(true)) - { - internalReader = new ReadDataHandler(ReadingNotAvailable); - } - - /// - /// Creates a new Zip input stream, for reading a zip archive. - /// - /// The underlying providing data. - /// Size of the buffer. - public ZipInputStream(Stream baseInputStream, int bufferSize) - : base(baseInputStream, new Inflater(true), bufferSize) - { - internalReader = new ReadDataHandler(ReadingNotAvailable); - } - - #endregion Constructors - - /// - /// Optional password used for encryption when non-null - /// - /// A password for all encrypted entries in this - public string Password - { - get - { - return password; - } - set - { - password = value; - } - } - - /// - /// Gets a value indicating if there is a current entry and it can be decompressed - /// - /// - /// The entry can only be decompressed if the library supports the zip features required to extract it. - /// See the ZipEntry Version property for more details. - /// - /// Since uses the local headers for extraction, entries with no compression combined with the - /// flag set, cannot be extracted as the end of the entry data cannot be deduced. - /// - public bool CanDecompressEntry - => entry != null - && IsEntryCompressionMethodSupported(entry) - && entry.CanDecompress - && (!entry.HasFlag(GeneralBitFlags.Descriptor) || entry.CompressionMethod != CompressionMethod.Stored || entry.IsCrypted); - - /// - /// Is the compression method for the specified entry supported? - /// - /// - /// Uses entry.CompressionMethodForHeader so that entries of type WinZipAES will be rejected. - /// - /// the entry to check. - /// true if the compression method is supported, false if not. - private static bool IsEntryCompressionMethodSupported(ZipEntry entry) - { - var entryCompressionMethod = entry.CompressionMethodForHeader; - - return entryCompressionMethod == CompressionMethod.Deflated || - entryCompressionMethod == CompressionMethod.Stored; - } - - /// - /// Advances to the next entry in the archive - /// - /// - /// The next entry in the archive or null if there are no more entries. - /// - /// - /// If the previous entry is still open CloseEntry is called. - /// - /// - /// Input stream is closed - /// - /// - /// Password is not set, password is invalid, compression method is invalid, - /// version required to extract is not supported - /// - public ZipEntry GetNextEntry() - { - if (crc == null) - { - throw new InvalidOperationException("Closed."); - } - - if (entry != null) - { - CloseEntry(); - } - - int header = inputBuffer.ReadLeInt(); - - if (header == ZipConstants.CentralHeaderSignature || - header == ZipConstants.EndOfCentralDirectorySignature || - header == ZipConstants.CentralHeaderDigitalSignature || - header == ZipConstants.ArchiveExtraDataSignature || - header == ZipConstants.Zip64CentralFileHeaderSignature) - { - // No more individual entries exist - Dispose(); - return null; - } - - // -jr- 07-Dec-2003 Ignore spanning temporary signatures if found - // Spanning signature is same as descriptor signature and is untested as yet. - if ((header == ZipConstants.SpanningTempSignature) || (header == ZipConstants.SpanningSignature)) - { - header = inputBuffer.ReadLeInt(); - } - - if (header != ZipConstants.LocalHeaderSignature) - { - throw new ZipException("Wrong Local header signature: 0x" + String.Format("{0:X}", header)); - } - - var versionRequiredToExtract = (short)inputBuffer.ReadLeShort(); - - flags = inputBuffer.ReadLeShort(); - method = (CompressionMethod)inputBuffer.ReadLeShort(); - var dostime = (uint)inputBuffer.ReadLeInt(); - int crc2 = inputBuffer.ReadLeInt(); - csize = inputBuffer.ReadLeInt(); - size = inputBuffer.ReadLeInt(); - int nameLen = inputBuffer.ReadLeShort(); - int extraLen = inputBuffer.ReadLeShort(); - - bool isCrypted = (flags & 1) == 1; - - byte[] buffer = new byte[nameLen]; - inputBuffer.ReadRawBuffer(buffer); - - var entryEncoding = _stringCodec.ZipInputEncoding(flags); - string name = entryEncoding.GetString(buffer); - var unicode = entryEncoding.IsZipUnicode(); - - entry = new ZipEntry(name, versionRequiredToExtract, ZipConstants.VersionMadeBy, method, unicode) - { - Flags = flags, - }; - - if ((flags & 8) == 0) - { - entry.Crc = crc2 & 0xFFFFFFFFL; - entry.Size = size & 0xFFFFFFFFL; - entry.CompressedSize = csize & 0xFFFFFFFFL; - - entry.CryptoCheckValue = (byte)((crc2 >> 24) & 0xff); - } - else - { - // This allows for GNU, WinZip and possibly other archives, the PKZIP spec - // says these values are zero under these circumstances. - if (crc2 != 0) - { - entry.Crc = crc2 & 0xFFFFFFFFL; - } - - if (size != 0) - { - entry.Size = size & 0xFFFFFFFFL; - } - - if (csize != 0) - { - entry.CompressedSize = csize & 0xFFFFFFFFL; - } - - entry.CryptoCheckValue = (byte)((dostime >> 8) & 0xff); - } - - entry.DosTime = dostime; - - // If local header requires Zip64 is true then the extended header should contain - // both values. - - // Handle extra data if present. This can set/alter some fields of the entry. - if (extraLen > 0) - { - byte[] extra = new byte[extraLen]; - inputBuffer.ReadRawBuffer(extra); - entry.ExtraData = extra; - } - - entry.ProcessExtraData(true); - if (entry.CompressedSize >= 0) - { - csize = entry.CompressedSize; - } - - if (entry.Size >= 0) - { - size = entry.Size; - } - - if (method == CompressionMethod.Stored && (!isCrypted && csize != size || (isCrypted && csize - ZipConstants.CryptoHeaderSize != size))) - { - throw new ZipException("Stored, but compressed != uncompressed"); - } - - // Determine how to handle reading of data if this is attempted. - if (IsEntryCompressionMethodSupported(entry)) - { - internalReader = new ReadDataHandler(InitialRead); - } - else - { - internalReader = new ReadDataHandler(ReadingNotSupported); - } - - return entry; - } - - /// - /// Read data descriptor at the end of compressed data. - /// - private void ReadDataDescriptor() - { - if (inputBuffer.ReadLeInt() != ZipConstants.DataDescriptorSignature) - { - throw new ZipException("Data descriptor signature not found"); - } - - entry.Crc = inputBuffer.ReadLeInt() & 0xFFFFFFFFL; - - if (entry.LocalHeaderRequiresZip64) - { - csize = inputBuffer.ReadLeLong(); - size = inputBuffer.ReadLeLong(); - } - else - { - csize = inputBuffer.ReadLeInt(); - size = inputBuffer.ReadLeInt(); - } - entry.CompressedSize = csize; - entry.Size = size; - } - - /// - /// Complete cleanup as the final part of closing. - /// - /// True if the crc value should be tested - private void CompleteCloseEntry(bool testCrc) - { - StopDecrypting(); - - if ((flags & 8) != 0) - { - ReadDataDescriptor(); - } - - size = 0; - - if (testCrc && - ((crc.Value & 0xFFFFFFFFL) != entry.Crc) && (entry.Crc != -1)) - { - throw new ZipException("CRC mismatch"); - } - - crc.Reset(); - - if (method == CompressionMethod.Deflated) - { - inf.Reset(); - } - entry = null; - } - - /// - /// Closes the current zip entry and moves to the next one. - /// - /// - /// The stream is closed - /// - /// - /// The Zip stream ends early - /// - public void CloseEntry() - { - if (crc == null) - { - throw new InvalidOperationException("Closed"); - } - - if (entry == null) - { - return; - } - - if (method == CompressionMethod.Deflated) - { - if ((flags & 8) != 0) - { - // We don't know how much we must skip, read until end. - byte[] tmp = new byte[4096]; - - // Read will close this entry - while (Read(tmp, 0, tmp.Length) > 0) - { - } - return; - } - - csize -= inf.TotalIn; - inputBuffer.Available += inf.RemainingInput; - } - - if ((inputBuffer.Available > csize) && (csize >= 0)) - { - inputBuffer.Available = (int)((long)inputBuffer.Available - csize); - } - else - { - csize -= inputBuffer.Available; - inputBuffer.Available = 0; - while (csize != 0) - { - long skipped = Skip(csize); - - if (skipped <= 0) - { - throw new ZipException("Zip archive ends early."); - } - - csize -= skipped; - } - } - - CompleteCloseEntry(false); - } - - /// - /// Returns 1 if there is an entry available - /// Otherwise returns 0. - /// - public override int Available - { - get - { - return entry != null ? 1 : 0; - } - } - - /// - /// Returns the current size that can be read from the current entry if available - /// - /// Thrown if the entry size is not known. - /// Thrown if no entry is currently available. - public override long Length - { - get - { - if (entry != null) - { - if (entry.Size >= 0) - { - return entry.Size; - } - else - { - throw new ZipException("Length not available for the current entry"); - } - } - else - { - throw new InvalidOperationException("No current entry"); - } - } - } - - /// - /// Reads a byte from the current zip entry. - /// - /// - /// The byte or -1 if end of stream is reached. - /// - public override int ReadByte() - { - byte[] b = new byte[1]; - if (Read(b, 0, 1) <= 0) - { - return -1; - } - return b[0] & 0xff; - } - - /// - /// Handle attempts to read by throwing an . - /// - /// The destination array to store data in. - /// The offset at which data read should be stored. - /// The maximum number of bytes to read. - /// Returns the number of bytes actually read. - private int ReadingNotAvailable(byte[] destination, int offset, int count) - { - throw new InvalidOperationException("Unable to read from this stream"); - } - - /// - /// Handle attempts to read from this entry by throwing an exception - /// - private int ReadingNotSupported(byte[] destination, int offset, int count) - { - throw new ZipException("The compression method for this entry is not supported"); - } - - /// - /// Handle attempts to read from this entry by throwing an exception - /// - private int StoredDescriptorEntry(byte[] destination, int offset, int count) => - throw new StreamUnsupportedException( - "The combination of Stored compression method and Descriptor flag is not possible to read using ZipInputStream"); - - - /// - /// Perform the initial read on an entry which may include - /// reading encryption headers and setting up inflation. - /// - /// The destination to fill with data read. - /// The offset to start reading at. - /// The maximum number of bytes to read. - /// The actual number of bytes read. - private int InitialRead(byte[] destination, int offset, int count) - { - var usesDescriptor = (entry.Flags & (int)GeneralBitFlags.Descriptor) != 0; - - // Handle encryption if required. - if (entry.IsCrypted) - { - if (password == null) - { - throw new ZipException("No password set."); - } - - // Generate and set crypto transform... - var managed = new PkzipClassicManaged(); - byte[] key = PkzipClassic.GenerateKeys(_stringCodec.ZipCryptoEncoding.GetBytes(password)); - - inputBuffer.CryptoTransform = managed.CreateDecryptor(key, null); - - byte[] cryptbuffer = new byte[ZipConstants.CryptoHeaderSize]; - inputBuffer.ReadClearTextBuffer(cryptbuffer, 0, ZipConstants.CryptoHeaderSize); - - if (cryptbuffer[ZipConstants.CryptoHeaderSize - 1] != entry.CryptoCheckValue) - { - throw new ZipException("Invalid password"); - } - - if (csize >= ZipConstants.CryptoHeaderSize) - { - csize -= ZipConstants.CryptoHeaderSize; - } - else if (!usesDescriptor) - { - throw new ZipException($"Entry compressed size {csize} too small for encryption"); - } - } - else - { - inputBuffer.CryptoTransform = null; - } - - if (csize > 0 || usesDescriptor) - { - if (method == CompressionMethod.Deflated && inputBuffer.Available > 0) - { - inputBuffer.SetInflaterInput(inf); - } - - // It's not possible to know how many bytes to read when using "Stored" compression (unless using encryption) - if (!entry.IsCrypted && method == CompressionMethod.Stored && usesDescriptor) - { - internalReader = StoredDescriptorEntry; - return StoredDescriptorEntry(destination, offset, count); - } - - if (!CanDecompressEntry) - { - internalReader = ReadingNotSupported; - return ReadingNotSupported(destination, offset, count); - } - - internalReader = BodyRead; - return BodyRead(destination, offset, count); - } - - - internalReader = ReadingNotAvailable; - return 0; - } - - /// - /// Read a block of bytes from the stream. - /// - /// The destination for the bytes. - /// The index to start storing data. - /// The number of bytes to attempt to read. - /// Returns the number of bytes read. - /// Zero bytes read means end of stream. - public override int Read(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative"); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative"); - } - - if ((buffer.Length - offset) < count) - { - throw new ArgumentException("Invalid offset/count combination"); - } - - return internalReader(buffer, offset, count); - } - - /// - /// Reads a block of bytes from the current zip entry. - /// - /// - /// The number of bytes read (this may be less than the length requested, even before the end of stream), or 0 on end of stream. - /// - /// - /// An i/o error occurred. - /// - /// - /// The deflated stream is corrupted. - /// - /// - /// The stream is not open. - /// - private int BodyRead(byte[] buffer, int offset, int count) - { - if (crc == null) - { - throw new InvalidOperationException("Closed"); - } - - if ((entry == null) || (count <= 0)) - { - return 0; - } - - if (offset + count > buffer.Length) - { - throw new ArgumentException("Offset + count exceeds buffer size"); - } - - bool finished = false; - - switch (method) - { - case CompressionMethod.Deflated: - count = base.Read(buffer, offset, count); - if (count <= 0) - { - if (!inf.IsFinished) - { - throw new ZipException("Inflater not finished!"); - } - inputBuffer.Available = inf.RemainingInput; - - // A csize of -1 is from an unpatched local header - if ((flags & 8) == 0 && - (inf.TotalIn != csize && csize != 0xFFFFFFFF && csize != -1 || inf.TotalOut != size)) - { - throw new ZipException("Size mismatch: " + csize + ";" + size + " <-> " + inf.TotalIn + ";" + inf.TotalOut); - } - inf.Reset(); - finished = true; - } - break; - - case CompressionMethod.Stored: - if ((count > csize) && (csize >= 0)) - { - count = (int)csize; - } - - if (count > 0) - { - count = inputBuffer.ReadClearTextBuffer(buffer, offset, count); - if (count > 0) - { - csize -= count; - size -= count; - } - } - - if (csize == 0) - { - finished = true; - } - else - { - if (count < 0) - { - throw new ZipException("EOF in stored block"); - } - } - break; - } - - if (count > 0) - { - crc.Update(new ArraySegment(buffer, offset, count)); - } - - if (finished) - { - CompleteCloseEntry(true); - } - - return count; - } - - /// - /// Closes the zip input stream - /// - protected override void Dispose(bool disposing) - { - internalReader = new ReadDataHandler(ReadingNotAvailable); - crc = null; - entry = null; - - base.Dispose(disposing); - } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipNameTransform.cs b/ICSharpCode.SharpZipLib/Zip/ZipNameTransform.cs deleted file mode 100644 index f91b20c3df67..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipNameTransform.cs +++ /dev/null @@ -1,313 +0,0 @@ -using ICSharpCode.SharpZipLib.Core; -using System; -using System.IO; -using System.Text; - -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// ZipNameTransform transforms names as per the Zip file naming convention. - /// - /// The use of absolute names is supported although its use is not valid - /// according to Zip naming conventions, and should not be used if maximum compatability is desired. - public class ZipNameTransform : INameTransform - { - #region Constructors - - /// - /// Initialize a new instance of - /// - public ZipNameTransform() - { - } - - /// - /// Initialize a new instance of - /// - /// The string to trim from the front of paths if found. - public ZipNameTransform(string trimPrefix) - { - TrimPrefix = trimPrefix; - } - - #endregion Constructors - - /// - /// Static constructor. - /// - static ZipNameTransform() - { - char[] invalidPathChars; - invalidPathChars = Path.GetInvalidPathChars(); - int howMany = invalidPathChars.Length + 2; - - InvalidEntryCharsRelaxed = new char[howMany]; - Array.Copy(invalidPathChars, 0, InvalidEntryCharsRelaxed, 0, invalidPathChars.Length); - InvalidEntryCharsRelaxed[howMany - 1] = '*'; - InvalidEntryCharsRelaxed[howMany - 2] = '?'; - - howMany = invalidPathChars.Length + 4; - InvalidEntryChars = new char[howMany]; - Array.Copy(invalidPathChars, 0, InvalidEntryChars, 0, invalidPathChars.Length); - InvalidEntryChars[howMany - 1] = ':'; - InvalidEntryChars[howMany - 2] = '\\'; - InvalidEntryChars[howMany - 3] = '*'; - InvalidEntryChars[howMany - 4] = '?'; - } - - /// - /// Transform a windows directory name according to the Zip file naming conventions. - /// - /// The directory name to transform. - /// The transformed name. - public string TransformDirectory(string name) - { - name = TransformFile(name); - if (name.Length > 0) - { - if (!name.EndsWith("/", StringComparison.Ordinal)) - { - name += "/"; - } - } - else - { - throw new ZipException("Cannot have an empty directory name"); - } - return name; - } - - /// - /// Transform a windows file name according to the Zip file naming conventions. - /// - /// The file name to transform. - /// The transformed name. - public string TransformFile(string name) - { - if (name != null) - { - string lowerName = name.ToLower(); - if ((trimPrefix_ != null) && (lowerName.IndexOf(trimPrefix_, StringComparison.Ordinal) == 0)) - { - name = name.Substring(trimPrefix_.Length); - } - - name = name.Replace(@"\", "/"); - name = PathUtils.DropPathRoot(name); - - // Drop any leading and trailing slashes. - name = name.Trim('/'); - - // Convert consecutive // characters to / - int index = name.IndexOf("//", StringComparison.Ordinal); - while (index >= 0) - { - name = name.Remove(index, 1); - index = name.IndexOf("//", StringComparison.Ordinal); - } - - name = MakeValidName(name, '_'); - } - else - { - name = string.Empty; - } - return name; - } - - /// - /// Get/set the path prefix to be trimmed from paths if present. - /// - /// The prefix is trimmed before any conversion from - /// a windows path is done. - public string TrimPrefix - { - get { return trimPrefix_; } - set - { - trimPrefix_ = value; - if (trimPrefix_ != null) - { - trimPrefix_ = trimPrefix_.ToLower(); - } - } - } - - /// - /// Force a name to be valid by replacing invalid characters with a fixed value - /// - /// The name to force valid - /// The replacement character to use. - /// Returns a valid name - private static string MakeValidName(string name, char replacement) - { - int index = name.IndexOfAny(InvalidEntryChars); - if (index >= 0) - { - var builder = new StringBuilder(name); - - while (index >= 0) - { - builder[index] = replacement; - - if (index >= name.Length) - { - index = -1; - } - else - { - index = name.IndexOfAny(InvalidEntryChars, index + 1); - } - } - name = builder.ToString(); - } - - if (name.Length > 0xffff) - { - throw new PathTooLongException(); - } - - return name; - } - - /// - /// Test a name to see if it is a valid name for a zip entry. - /// - /// The name to test. - /// If true checking is relaxed about windows file names and absolute paths. - /// Returns true if the name is a valid zip name; false otherwise. - /// Zip path names are actually in Unix format, and should only contain relative paths. - /// This means that any path stored should not contain a drive or - /// device letter, or a leading slash. All slashes should forward slashes '/'. - /// An empty name is valid for a file where the input comes from standard input. - /// A null name is not considered valid. - /// - public static bool IsValidName(string name, bool relaxed) - { - bool result = (name != null); - - if (result) - { - if (relaxed) - { - result = name.IndexOfAny(InvalidEntryCharsRelaxed) < 0; - } - else - { - result = - (name.IndexOfAny(InvalidEntryChars) < 0) && - (name.IndexOf('/') != 0); - } - } - - return result; - } - - /// - /// Test a name to see if it is a valid name for a zip entry. - /// - /// The name to test. - /// Returns true if the name is a valid zip name; false otherwise. - /// Zip path names are actually in unix format, - /// and should only contain relative paths if a path is present. - /// This means that the path stored should not contain a drive or - /// device letter, or a leading slash. All slashes should forward slashes '/'. - /// An empty name is valid where the input comes from standard input. - /// A null name is not considered valid. - /// - public static bool IsValidName(string name) - { - bool result = - (name != null) && - (name.IndexOfAny(InvalidEntryChars) < 0) && - (name.IndexOf('/') != 0) - ; - return result; - } - - #region Instance Fields - - private string trimPrefix_; - - #endregion Instance Fields - - #region Class Fields - - private static readonly char[] InvalidEntryChars; - private static readonly char[] InvalidEntryCharsRelaxed; - - #endregion Class Fields - } - - /// - /// An implementation of INameTransform that transforms entry paths as per the Zip file naming convention. - /// Strips path roots and puts directory separators in the correct format ('/') - /// - public class PathTransformer : INameTransform - { - /// - /// Initialize a new instance of - /// - public PathTransformer() - { - } - - /// - /// Transform a windows directory name according to the Zip file naming conventions. - /// - /// The directory name to transform. - /// The transformed name. - public string TransformDirectory(string name) - { - name = TransformFile(name); - - if (name.Length > 0) - { - if (!name.EndsWith("/", StringComparison.Ordinal)) - { - name += "/"; - } - } - else - { - throw new ZipException("Cannot have an empty directory name"); - } - - return name; - } - - /// - /// Transform a windows file name according to the Zip file naming conventions. - /// - /// The file name to transform. - /// The transformed name. - public string TransformFile(string name) - { - if (name != null) - { - // Put separators in the expected format. - name = name.Replace(@"\", "/"); - - // Remove the path root. - name = PathUtils.DropPathRoot(name); - - // Drop any leading and trailing slashes. - name = name.Trim('/'); - - // Convert consecutive // characters to / - int index = name.IndexOf("//", StringComparison.Ordinal); - while (index >= 0) - { - name = name.Remove(index, 1); - index = name.IndexOf("//", StringComparison.Ordinal); - } - } - else - { - name = string.Empty; - } - - return name; - } - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs b/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs deleted file mode 100644 index 75f1184afcfc..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs +++ /dev/null @@ -1,1004 +0,0 @@ -using ICSharpCode.SharpZipLib.Checksum; -using ICSharpCode.SharpZipLib.Core; -using ICSharpCode.SharpZipLib.Encryption; -using ICSharpCode.SharpZipLib.Zip.Compression; -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; - -namespace ICSharpCode.SharpZipLib.Zip -{ - /// - /// This is a DeflaterOutputStream that writes the files into a zip - /// archive one after another. It has a special method to start a new - /// zip entry. The zip entries contains information about the file name - /// size, compressed size, CRC, etc. - /// - /// It includes support for Stored and Deflated entries. - /// This class is not thread safe. - ///
- ///
Author of the original java version : Jochen Hoenicke - ///
- /// This sample shows how to create a zip file - /// - /// using System; - /// using System.IO; - /// - /// using ICSharpCode.SharpZipLib.Core; - /// using ICSharpCode.SharpZipLib.Zip; - /// - /// class MainClass - /// { - /// public static void Main(string[] args) - /// { - /// string[] filenames = Directory.GetFiles(args[0]); - /// byte[] buffer = new byte[4096]; - /// - /// using ( ZipOutputStream s = new ZipOutputStream(File.Create(args[1])) ) { - /// - /// s.SetLevel(9); // 0 - store only to 9 - means best compression - /// - /// foreach (string file in filenames) { - /// ZipEntry entry = new ZipEntry(file); - /// s.PutNextEntry(entry); - /// - /// using (FileStream fs = File.OpenRead(file)) { - /// StreamUtils.Copy(fs, s, buffer); - /// } - /// } - /// } - /// } - /// } - /// - /// - public class ZipOutputStream : DeflaterOutputStream - { - #region Constructors - - /// - /// Creates a new Zip output stream, writing a zip archive. - /// - /// - /// The output stream to which the archive contents are written. - /// - public ZipOutputStream(Stream baseOutputStream) - : base(baseOutputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true)) - { - } - - /// - /// Creates a new Zip output stream, writing a zip archive. - /// - /// The output stream to which the archive contents are written. - /// Size of the buffer to use. - public ZipOutputStream(Stream baseOutputStream, int bufferSize) - : base(baseOutputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true), bufferSize) - { - } - - internal ZipOutputStream(Stream baseOutputStream, StringCodec stringCodec) : this(baseOutputStream) - { - _stringCodec = stringCodec; - } - - /// - /// - /// - /// - /// - public ZipOutputStream(Stream baseOutputStream, bool existing) - : base(baseOutputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true)) - { - if (existing) - { - using (var zipFile = new ZipFile(baseOutputStream)) - { - zipFile.IsStreamOwner = false; - entries = zipFile.OfType().ToList(); - offset = zipFile.GetCentralDirOffset(); - baseOutputStream_.Seek(offset, SeekOrigin.Begin); - } - } - } - - #endregion Constructors - - /// - /// Gets a flag value of true if the central header has been added for this archive; false if it has not been added. - /// - /// No further entries can be added once this has been done. - public bool IsFinished - { - get - { - return entries == null; - } - } - - /// - /// Set the zip file comment. - /// - /// - /// The comment text for the entire archive. - /// - /// - /// The converted comment is longer than 0xffff bytes. - /// - public void SetComment(string comment) - { - byte[] commentBytes = _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment); - if (commentBytes.Length > 0xffff) - { - throw new ArgumentOutOfRangeException(nameof(comment)); - } - zipComment = commentBytes; - } - - /// - /// Sets the compression level. The new level will be activated - /// immediately. - /// - /// The new compression level (1 to 9). - /// - /// Level specified is not supported. - /// - /// - public void SetLevel(int level) - { - deflater_.SetLevel(level); - defaultCompressionLevel = level; - } - - /// - /// Get the current deflater compression level - /// - /// The current compression level - public int GetLevel() - { - return deflater_.GetLevel(); - } - - /// - /// Get / set a value indicating how Zip64 Extension usage is determined when adding entries. - /// - /// Older archivers may not understand Zip64 extensions. - /// If backwards compatability is an issue be careful when adding entries to an archive. - /// Setting this property to off is workable but less desirable as in those circumstances adding a file - /// larger then 4GB will fail. - public UseZip64 UseZip64 - { - get { return useZip64_; } - set { useZip64_ = value; } - } - - /// - /// Used for transforming the names of entries added by . - /// Defaults to , set to null to disable transforms and use names as supplied. - /// - public INameTransform NameTransform { get; set; } = new PathTransformer(); - - /// - /// Get/set the password used for encryption. - /// - /// When set to null or if the password is empty no encryption is performed - public string Password - { - get - { - return password; - } - set - { - if ((value != null) && (value.Length == 0)) - { - password = null; - } - else - { - password = value; - } - } - } - - /// - /// Write an unsigned short in little endian byte order. - /// - private void WriteLeShort(int value) - { - unchecked - { - baseOutputStream_.WriteByte((byte)(value & 0xff)); - baseOutputStream_.WriteByte((byte)((value >> 8) & 0xff)); - } - } - - /// - /// Write an int in little endian byte order. - /// - private void WriteLeInt(int value) - { - unchecked - { - WriteLeShort(value); - WriteLeShort(value >> 16); - } - } - - /// - /// Write an int in little endian byte order. - /// - private void WriteLeLong(long value) - { - unchecked - { - WriteLeInt((int)value); - WriteLeInt((int)(value >> 32)); - } - } - - // Apply any configured transforms/cleaning to the name of the supplied entry. - private void TransformEntryName(ZipEntry entry) - { - if (NameTransform == null) return; - entry.Name = entry.IsDirectory - ? NameTransform.TransformDirectory(entry.Name) - : NameTransform.TransformFile(entry.Name); - } - - /// - /// Starts a new Zip entry. It automatically closes the previous - /// entry if present. - /// All entry elements bar name are optional, but must be correct if present. - /// If the compression method is stored and the output is not patchable - /// the compression for that entry is automatically changed to deflate level 0 - /// - /// - /// the entry. - /// - /// - /// if entry passed is null. - /// - /// - /// if an I/O error occurred. - /// - /// - /// if stream was finished - /// - /// - /// Too many entries in the Zip file
- /// Entry name is too long
- /// Finish has already been called
- ///
- /// - /// The Compression method specified for the entry is unsupported. - /// - public void PutNextEntry(ZipEntry entry) - { - if (curEntry != null) - { - CloseEntry(); - } - - PutNextEntry(baseOutputStream_, entry); - - if (entry.IsCrypted) - { - WriteOutput(GetEntryEncryptionHeader(entry)); - } - } - - /// - /// Starts a new passthrough Zip entry. It automatically closes the previous - /// entry if present. - /// Passthrough entry is an entry that is created from compressed data. - /// It is useful to avoid recompression to save CPU resources if compressed data is already disposable. - /// All entry elements bar name, crc, size and compressed size are optional, but must be correct if present. - /// Compression should be set to Deflated. - /// - /// - /// the entry. - /// - /// - /// if entry passed is null. - /// - /// - /// if an I/O error occurred. - /// - /// - /// if stream was finished. - /// - /// - /// Crc is not set
- /// Size is not set
- /// CompressedSize is not set
- /// CompressionMethod is not Deflate
- /// Too many entries in the Zip file
- /// Entry name is too long
- /// Finish has already been called
- ///
- /// - /// The Compression method specified for the entry is unsupported
- /// Entry is encrypted
- ///
- public void PutNextPassthroughEntry(ZipEntry entry) - { - if (curEntry != null) - { - CloseEntry(); - } - - if (entry.Crc < 0) - { - throw new ZipException("Crc must be set for passthrough entry"); - } - - if (entry.Size < 0) - { - throw new ZipException("Size must be set for passthrough entry"); - } - - if (entry.CompressedSize < 0) - { - throw new ZipException("CompressedSize must be set for passthrough entry"); - } - - if (entry.CompressionMethod != CompressionMethod.Deflated) - { - throw new NotImplementedException("Only Deflated entries are supported for passthrough"); - } - - if (!string.IsNullOrEmpty(Password)) - { - throw new NotImplementedException("Encrypted passthrough entries are not supported"); - } - - PutNextEntry(baseOutputStream_, entry, 0, true); - } - - - private void WriteOutput(byte[] bytes) - => baseOutputStream_.Write(bytes, 0, bytes.Length); - - private Task WriteOutputAsync(byte[] bytes) - => baseOutputStream_.WriteAsync(bytes, 0, bytes.Length); - - private byte[] GetEntryEncryptionHeader(ZipEntry entry) => - entry.AESKeySize > 0 - ? InitializeAESPassword(entry, Password) - : CreateZipCryptoHeader(entry.Crc < 0 ? entry.DosTime << 16 : entry.Crc); - - internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0, bool passthroughEntry = false) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - if (entries == null) - { - throw new InvalidOperationException("ZipOutputStream was finished"); - } - - if (entries.Count == int.MaxValue) - { - throw new ZipException("Too many entries for Zip file"); - } - - CompressionMethod method = entry.CompressionMethod; - - // Check that the compression is one that we support - if (method != CompressionMethod.Deflated && method != CompressionMethod.Stored) - { - throw new NotImplementedException("Compression method not supported"); - } - - // A password must have been set in order to add AES encrypted entries - if (entry.AESKeySize > 0 && string.IsNullOrEmpty(this.Password)) - { - throw new InvalidOperationException("The Password property must be set before AES encrypted entries can be added"); - } - - entryIsPassthrough = passthroughEntry; - - int compressionLevel = defaultCompressionLevel; - - // Clear flags that the library manages internally - entry.Flags &= (int)GeneralBitFlags.UnicodeText; - patchEntryHeader = false; - - bool headerInfoAvailable; - - // No need to compress - definitely no data. - if (entry.Size == 0 && !entryIsPassthrough) - { - entry.CompressedSize = entry.Size; - entry.Crc = 0; - method = CompressionMethod.Stored; - headerInfoAvailable = true; - } - else - { - headerInfoAvailable = (entry.Size >= 0) && entry.HasCrc && entry.CompressedSize >= 0; - - // Switch to deflation if storing isnt possible. - if (method == CompressionMethod.Stored) - { - if (!headerInfoAvailable) - { - if (!CanPatchEntries) - { - // Can't patch entries so storing is not possible. - method = CompressionMethod.Deflated; - compressionLevel = 0; - } - } - else // entry.size must be > 0 - { - entry.CompressedSize = entry.Size; - headerInfoAvailable = entry.HasCrc; - } - } - } - - if (headerInfoAvailable == false) - { - if (CanPatchEntries == false) - { - // Only way to record size and compressed size is to append a data descriptor - // after compressed data. - - // Stored entries of this form have already been converted to deflating. - entry.Flags |= 8; - } - else - { - patchEntryHeader = true; - } - } - - if (Password != null) - { - entry.IsCrypted = true; - if (entry.Crc < 0) - { - // Need to append a data descriptor as the crc isnt available for use - // with encryption, the date is used instead. Setting the flag - // indicates this to the decompressor. - entry.Flags |= 8; - } - } - - entry.Offset = offset; - entry.CompressionMethod = (CompressionMethod)method; - - curMethod = method; - - if ((useZip64_ == UseZip64.On) || ((entry.Size < 0) && (useZip64_ == UseZip64.Dynamic))) - { - entry.ForceZip64(); - } - - // Apply any required transforms to the entry name - TransformEntryName(entry); - - // Write the local file header - offset += ZipFormat.WriteLocalHeader(stream, entry, out var entryPatchData, - headerInfoAvailable, patchEntryHeader, streamOffset, _stringCodec); - - patchData = entryPatchData; - - // Fix offsetOfCentraldir for AES - if (entry.AESKeySize > 0) - offset += entry.AESOverheadSize; - - // Activate the entry. - curEntry = entry; - size = 0; - - if (entryIsPassthrough) - return; - - crc.Reset(); - if (method == CompressionMethod.Deflated) - { - deflater_.Reset(); - deflater_.SetLevel(compressionLevel); - } - } - - /// - /// Starts a new Zip entry. It automatically closes the previous - /// entry if present. - /// All entry elements bar name are optional, but must be correct if present. - /// If the compression method is stored and the output is not patchable - /// the compression for that entry is automatically changed to deflate level 0 - /// - /// - /// the entry. - /// - /// The that can be used to cancel the operation. - /// - /// if entry passed is null. - /// - /// - /// if an I/O error occured. - /// - /// - /// if stream was finished - /// - /// - /// Too many entries in the Zip file
- /// Entry name is too long
- /// Finish has already been called
- ///
- /// - /// The Compression method specified for the entry is unsupported. - /// - public async Task PutNextEntryAsync(ZipEntry entry, CancellationToken ct = default) - { - if (curEntry != null) await CloseEntryAsync(ct); - await baseOutputStream_.WriteProcToStreamAsync(s => - { - PutNextEntry(s, entry, baseOutputStream_.Position); - }, ct); - - if (!entry.IsCrypted) return; - await WriteOutputAsync(GetEntryEncryptionHeader(entry)); - } - - /// - /// Closes the current entry, updating header and footer information as required - /// - /// - /// Invalid entry field values. - /// - /// - /// An I/O error occurs. - /// - /// - /// No entry is active. - /// - public void CloseEntry() - { - WriteEntryFooter(baseOutputStream_); - - // Patch the header if possible - if (patchEntryHeader) - { - patchEntryHeader = false; - ZipFormat.PatchLocalHeaderSync(baseOutputStream_, curEntry, patchData); - } - - entries.Add(curEntry); - curEntry = null; - } - - /// - public async Task CloseEntryAsync(CancellationToken ct) - { - await baseOutputStream_.WriteProcToStreamAsync(WriteEntryFooter, ct); - - // Patch the header if possible - if (patchEntryHeader) - { - patchEntryHeader = false; - await ZipFormat.PatchLocalHeaderAsync(baseOutputStream_, curEntry, patchData, ct); - } - - entries.Add(curEntry); - curEntry = null; - } - - internal void WriteEntryFooter(Stream stream) - { - if (curEntry == null) - { - throw new InvalidOperationException("No open entry"); - } - - if (entryIsPassthrough) - { - if (curEntry.CompressedSize != size) - { - throw new ZipException($"compressed size was {size}, but {curEntry.CompressedSize} expected"); - } - - offset += size; - return; - } - - long csize = size; - - // First finish the deflater, if appropriate - if (curMethod == CompressionMethod.Deflated) - { - if (size >= 0) - { - base.Finish(); - csize = deflater_.TotalOut; - } - else - { - deflater_.Reset(); - } - } - else if (curMethod == CompressionMethod.Stored) - { - // This is done by Finish() for Deflated entries, but we need to do it - // ourselves for Stored ones - base.GetAuthCodeIfAES(); - } - - // Write the AES Authentication Code (a hash of the compressed and encrypted data) - if (curEntry.AESKeySize > 0) - { - stream.Write(AESAuthCode, 0, 10); - // Always use 0 as CRC for AE-2 format - curEntry.Crc = 0; - } - else - { - if (curEntry.Crc < 0) - { - curEntry.Crc = crc.Value; - } - else if (curEntry.Crc != crc.Value) - { - throw new ZipException($"crc was {crc.Value}, but {curEntry.Crc} was expected"); - } - } - - if (curEntry.Size < 0) - { - curEntry.Size = size; - } - else if (curEntry.Size != size) - { - throw new ZipException($"size was {size}, but {curEntry.Size} was expected"); - } - - if (curEntry.CompressedSize < 0) - { - curEntry.CompressedSize = csize; - } - else if (curEntry.CompressedSize != csize) - { - throw new ZipException($"compressed size was {csize}, but {curEntry.CompressedSize} expected"); - } - - offset += csize; - - if (curEntry.IsCrypted) - { - curEntry.CompressedSize += curEntry.EncryptionOverheadSize; - } - - // Add data descriptor if flagged as required - if ((curEntry.Flags & 8) != 0) - { - stream.WriteLEInt(ZipConstants.DataDescriptorSignature); - stream.WriteLEInt(unchecked((int)curEntry.Crc)); - - if (curEntry.LocalHeaderRequiresZip64) - { - stream.WriteLELong(curEntry.CompressedSize); - stream.WriteLELong(curEntry.Size); - offset += ZipConstants.Zip64DataDescriptorSize; - } - else - { - stream.WriteLEInt((int)curEntry.CompressedSize); - stream.WriteLEInt((int)curEntry.Size); - offset += ZipConstants.DataDescriptorSize; - } - } - } - - - - // File format for AES: - // Size (bytes) Content - // ------------ ------- - // Variable Salt value - // 2 Password verification value - // Variable Encrypted file data - // 10 Authentication code - // - // Value in the "compressed size" fields of the local file header and the central directory entry - // is the total size of all the items listed above. In other words, it is the total size of the - // salt value, password verification value, encrypted data, and authentication code. - - /// - /// Initializes encryption keys based on given password. - /// - protected byte[] InitializeAESPassword(ZipEntry entry, string rawPassword) - { - var salt = new byte[entry.AESSaltLen]; - // Salt needs to be cryptographically random, and unique per file - if (_aesRnd == null) - _aesRnd = RandomNumberGenerator.Create(); - _aesRnd.GetBytes(salt); - int blockSize = entry.AESKeySize / 8; // bits to bytes - - cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true); - - var headBytes = new byte[salt.Length + 2]; - - Array.Copy(salt, headBytes, salt.Length); - Array.Copy(((ZipAESTransform)cryptoTransform_).PwdVerifier, 0, - headBytes, headBytes.Length - 2, 2); - - return headBytes; - } - - private byte[] CreateZipCryptoHeader(long crcValue) - { - offset += ZipConstants.CryptoHeaderSize; - - InitializeZipCryptoPassword(Password); - - byte[] cryptBuffer = new byte[ZipConstants.CryptoHeaderSize]; - using (var rng = new RNGCryptoServiceProvider()) - { - rng.GetBytes(cryptBuffer); - } - - cryptBuffer[11] = (byte)(crcValue >> 24); - - EncryptBlock(cryptBuffer, 0, cryptBuffer.Length); - - return cryptBuffer; - } - - /// - /// Initializes encryption keys based on given . - /// - /// The password. - private void InitializeZipCryptoPassword(string password) - { - var pkManaged = new PkzipClassicManaged(); - byte[] key = PkzipClassic.GenerateKeys(ZipCryptoEncoding.GetBytes(password)); - cryptoTransform_ = pkManaged.CreateEncryptor(key, null); - } - - /// - /// Writes the given buffer to the current entry. - /// - /// The buffer containing data to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// Archive size is invalid - /// No entry is active. - public override void Write(byte[] buffer, int offset, int count) - { - if (curEntry == null) - { - throw new InvalidOperationException("No open entry."); - } - - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative"); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative"); - } - - if ((buffer.Length - offset) < count) - { - throw new ArgumentException("Invalid offset/count combination"); - } - - if (curEntry.AESKeySize == 0 && !entryIsPassthrough) - { - // Only update CRC if AES is not enabled and entry is not a passthrough one - crc.Update(new ArraySegment(buffer, offset, count)); - } - - size += count; - - if (curMethod == CompressionMethod.Stored || entryIsPassthrough) - { - if (Password != null) - { - CopyAndEncrypt(buffer, offset, count); - } - else - { - baseOutputStream_.Write(buffer, offset, count); - } - } - else - { - base.Write(buffer, offset, count); - } - } - - private void CopyAndEncrypt(byte[] buffer, int offset, int count) - { - const int CopyBufferSize = 4096; - byte[] localBuffer = new byte[CopyBufferSize]; - while (count > 0) - { - int bufferCount = (count < CopyBufferSize) ? count : CopyBufferSize; - - Array.Copy(buffer, offset, localBuffer, 0, bufferCount); - EncryptBlock(localBuffer, 0, bufferCount); - baseOutputStream_.Write(localBuffer, 0, bufferCount); - count -= bufferCount; - offset += bufferCount; - } - } - - /// - /// Finishes the stream. This will write the central directory at the - /// end of the zip file and flush the stream. - /// - /// - /// This is automatically called when the stream is closed. - /// - /// - /// An I/O error occurs. - /// - /// - /// Comment exceeds the maximum length
- /// Entry name exceeds the maximum length - ///
- public override void Finish() - { - if (entries == null) - { - return; - } - - if (curEntry != null) - { - CloseEntry(); - } - - long numEntries = entries.Count; - long sizeEntries = 0; - - foreach (var entry in entries) - { - sizeEntries += ZipFormat.WriteEndEntry(baseOutputStream_, entry, _stringCodec); - } - - ZipFormat.WriteEndOfCentralDirectory(baseOutputStream_, numEntries, sizeEntries, offset, zipComment); - - entries = null; - } - - /// > - public override async Task FinishAsync(CancellationToken ct) - { - using (var ms = new MemoryStream()) - { - if (entries == null) - { - return; - } - - if (curEntry != null) - { - await CloseEntryAsync(ct); - } - - long numEntries = entries.Count; - long sizeEntries = 0; - - foreach (var entry in entries) - { - await baseOutputStream_.WriteProcToStreamAsync(ms, s => - { - sizeEntries += ZipFormat.WriteEndEntry(s, entry, _stringCodec); - }, ct); - } - - await baseOutputStream_.WriteProcToStreamAsync(ms, s - => ZipFormat.WriteEndOfCentralDirectory(s, numEntries, sizeEntries, offset, zipComment), - ct); - - entries = null; - } - } - - /// - /// Flushes the stream by calling Flush on the deflater stream unless - /// the current compression method is . Then it flushes the underlying output stream. - /// - public override void Flush() - { - if (curMethod == CompressionMethod.Stored) - { - baseOutputStream_.Flush(); - } - else - { - base.Flush(); - } - } - - #region Instance Fields - - /// - /// The entries for the archive. - /// - private List entries = new List(); - - /// - /// Used to track the crc of data added to entries. - /// - private Crc32 crc = new Crc32(); - - /// - /// The current entry being added. - /// - private ZipEntry curEntry; - - private bool entryIsPassthrough; - - private int defaultCompressionLevel = Deflater.DEFAULT_COMPRESSION; - - private CompressionMethod curMethod = CompressionMethod.Deflated; - - /// - /// Used to track the size of data for an entry during writing. - /// - private long size; - - /// - /// Offset to be recorded for each entry in the central header. - /// - private long offset; - - /// - /// Comment for the entire archive recorded in central header. - /// - private byte[] zipComment = Empty.Array(); - - /// - /// Flag indicating that header patching is required for the current entry. - /// - private bool patchEntryHeader; - - /// - /// The values to patch in the entry local header - /// - private EntryPatchData patchData; - - // Default is dynamic which is not backwards compatible and can cause problems - // with XP's built in compression which cant read Zip64 archives. - // However it does avoid the situation were a large file is added and cannot be completed correctly. - // NOTE: Setting the size for entries before they are added is the best solution! - private UseZip64 useZip64_ = UseZip64.Dynamic; - - /// - /// The password to use when encrypting archive entries. - /// - private string password; - - private readonly StringCodec _stringCodec = ZipStrings.GetStringCodec(); - - #endregion Instance Fields - - #region Static Fields - - // Static to help ensure that multiple files within a zip will get different random salt - private static RandomNumberGenerator _aesRnd = RandomNumberGenerator.Create(); - - #endregion Static Fields - } -} diff --git a/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs b/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs deleted file mode 100644 index 29fa980147e7..000000000000 --- a/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs +++ /dev/null @@ -1,213 +0,0 @@ -using System; -using System.Text; -using ICSharpCode.SharpZipLib.Core; - -namespace ICSharpCode.SharpZipLib.Zip -{ - internal static class EncodingExtensions - { - public static bool IsZipUnicode(this Encoding e) - => e.Equals(StringCodec.UnicodeZipEncoding); - } - - /// - /// Deprecated way of setting zip encoding provided for backwards compability. - /// Use when possible. - /// - /// - /// If any ZipStrings properties are being modified, it will enter a backwards compatibility mode, mimicking the - /// old behaviour where a single instance was shared between all Zip* instances. - /// - public static class ZipStrings - { - static readonly StringCodec CompatCodec = new StringCodec(); - - private static bool compatibilityMode; - - /// - /// Returns a new instance or the shared backwards compatible instance. - /// - /// - public static StringCodec GetStringCodec() - => compatibilityMode ? CompatCodec : new StringCodec(); - - /// - [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] - public static int CodePage - { - get => CompatCodec.CodePage; - set - { - CompatCodec.CodePage = value; - compatibilityMode = true; - } - } - - /// - [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] - public static int SystemDefaultCodePage => StringCodec.SystemDefaultCodePage; - - /// - [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] - public static bool UseUnicode - { - get => !CompatCodec.ForceZipLegacyEncoding; - set - { - CompatCodec.ForceZipLegacyEncoding = !value; - compatibilityMode = true; - } - } - - /// - [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] - private static bool HasUnicodeFlag(int flags) - => ((GeneralBitFlags)flags).HasFlag(GeneralBitFlags.UnicodeText); - - /// - [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] - public static string ConvertToString(byte[] data, int count) - => CompatCodec.ZipOutputEncoding.GetString(data, 0, count); - - /// - [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] - public static string ConvertToString(byte[] data) - => CompatCodec.ZipOutputEncoding.GetString(data); - - /// - [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] - public static string ConvertToStringExt(int flags, byte[] data, int count) - => CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data, 0, count); - - /// - [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] - public static string ConvertToStringExt(int flags, byte[] data) - => CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data); - - /// - [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] - public static byte[] ConvertToArray(string str) - => ConvertToArray(0, str); - - /// - [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] - public static byte[] ConvertToArray(int flags, string str) - => (string.IsNullOrEmpty(str)) - ? Empty.Array() - : CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetBytes(str); - } - - /// - /// Utility class for resolving the encoding used for reading and writing strings - /// - public class StringCodec - { - static StringCodec() - { - try - { - var platformCodepage = Encoding.Default.CodePage; - SystemDefaultCodePage = (platformCodepage == 1 || platformCodepage == 2 || platformCodepage == 3 || platformCodepage == 42) ? FallbackCodePage : platformCodepage; - } - catch - { - SystemDefaultCodePage = FallbackCodePage; - } - - SystemDefaultEncoding = Encoding.GetEncoding(SystemDefaultCodePage); - } - - /// - /// If set, use the encoding set by for zip entries instead of the defaults - /// - public bool ForceZipLegacyEncoding { get; set; } - - /// - /// The default encoding used for ZipCrypto passwords in zip files, set to - /// for greatest compability. - /// - public static Encoding DefaultZipCryptoEncoding => SystemDefaultEncoding; - - /// - /// Returns the encoding for an output . - /// Unless overriden by it returns . - /// - public Encoding ZipOutputEncoding => ZipEncoding(!ForceZipLegacyEncoding); - - /// - /// Returns if is set, otherwise it returns the encoding indicated by - /// - public Encoding ZipEncoding(bool unicode) => unicode ? UnicodeZipEncoding : _legacyEncoding; - - /// - /// Returns the appropriate encoding for an input according to . - /// If overridden by , it always returns the encoding indicated by . - /// - /// - /// - public Encoding ZipInputEncoding(GeneralBitFlags flags) => ZipInputEncoding((int)flags); - - /// - public Encoding ZipInputEncoding(int flags) => ZipEncoding(!ForceZipLegacyEncoding && (flags & (int)GeneralBitFlags.UnicodeText) != 0); - - /// Code page encoding, used for non-unicode strings - /// - /// The original Zip specification (https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT) states - /// that file names should only be encoded with IBM Code Page 437 or UTF-8. - /// In practice, most zip apps use OEM or system encoding (typically cp437 on Windows). - /// Let's be good citizens and default to UTF-8 http://utf8everywhere.org/ - /// - private Encoding _legacyEncoding = SystemDefaultEncoding; - - private Encoding _zipArchiveCommentEncoding; - private Encoding _zipCryptoEncoding; - - /// - /// Returns the UTF-8 code page (65001) used for zip entries with unicode flag set - /// - public static readonly Encoding UnicodeZipEncoding = Encoding.UTF8; - - /// - /// Code page used for non-unicode strings and legacy zip encoding (if is set). - /// Default value is - /// - public int CodePage - { - get => _legacyEncoding.CodePage; - set => _legacyEncoding = (value < 4 || value > 65535 || value == 42) - ? throw new ArgumentOutOfRangeException(nameof(value)) - : Encoding.GetEncoding(value); - } - - private const int FallbackCodePage = 437; - - /// - /// Operating system default codepage, or if it could not be retrieved, the fallback code page IBM 437. - /// - public static int SystemDefaultCodePage { get; } - - /// - /// The system default encoding, based on - /// - public static Encoding SystemDefaultEncoding { get; } - - /// - /// The encoding used for the zip archive comment. Defaults to the encoding for , since - /// no unicode flag can be set for it in the files. - /// - public Encoding ZipArchiveCommentEncoding - { - get => _zipArchiveCommentEncoding ?? _legacyEncoding; - set => _zipArchiveCommentEncoding = value; - } - - /// - /// The encoding used for the ZipCrypto passwords. Defaults to . - /// - public Encoding ZipCryptoEncoding - { - get => _zipCryptoEncoding ?? DefaultZipCryptoEncoding; - set => _zipCryptoEncoding = value; - } - } -} diff --git a/ICSharpCode.SharpZipLib/assets/ICSharpCode.SharpZipLib.snk b/ICSharpCode.SharpZipLib/assets/ICSharpCode.SharpZipLib.snk deleted file mode 100644 index 58cf194dfdb9ac183edd43614d60de5ada399419..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098Gp-!m$opqVu=qeJDqA>&TAr3E+iVsF< zW3MCs`rDwr3N0M^KYF`#hqG3CPXtz~W@i;lXGyX1 zb?B}~=4AN^h_0Cpj+XbMoWaMn6QSKJrHllU7x*~1X~gvus8fe?!3>7i;&QznoP}U{ zz^^d~3jkzuMI!8@SU|_Ab$YY^?njPb#^@kt!}Cm5^kjS3{;DH`qQTUteSbDE`6xZ9 zG>cf2D#ne>782Mna@FZR&$2j-wll!(ga_d4_l&g~!2LEuu-NtFY*>nvqe7^RANsYV zzC+Y9a(P$Ol14we*lUP&>c`sM0pr3FRH zmQbg6*TC~B$I9s$X(&$X&Unt?2)3_%Jw83A{8o2)qPFEh4ri9t5>pj%G<7$0QO8L1 zwF_voriSbH=M5Bbyexi4;n^;N+CCcWHI^Ul%`sRTmbaJ?1mcW}oXciF_XpKaEjmZF z`5gsxG;W8%RN|N5qnZ>Zo!4ok^Yb{WM;>)n*3%chR;0n}Se4Sj;y55d%uBNVzE}{N?u1Wx~{M6M{!-o7=dtsQ! z-NV`&rX4n~;YCMz)XlLKFIXuVAMMAXulbf$a6B0gPgCj_N0~UC7ljd3n>FbYudyDUFgVOH-;}V_wfEkO)S`MEr}3oq5x_?l5@D@H zu!zP^@r{IJrdX-3@(pH=mcKUmL9GhmD5j0kp>~P!(Jf;ZQ#JQMA|u{%Czxt?dSoI` zgJzS{AECdpn!$wlLyf|exE&~8+Hz~**SI4VyQQ!JpfdHL{;hlofe^;${R2R9ICC&*-#Bq_N2yI0SeN{t4J`?et| zSh0ElhH*-~)>h~``zNCt1p=fZ`tvkPqmyw7?%env#R7LbxFHejtx4h*%w%i*O- z1x9m5%*UzFd4bZgtS$&~fwZ@~9-^K`zx%T@+&m0RRCktaQ7*#mJ1XpXxR)yy!4xOpgKSBIb?D@LW`07?2V_6fA6DtOUCh2`o!|UTV~uFs8byf>IVMHtCy) z@GJt-?{i{;+7TUoiWlw0ZZZm8If5fLU=7@$UKK-YW`$~XTAMhzv0S zlX;8aX8ef~9saATLB43@e)_-h+PupNoy9fj6N=@i54(S0?1LX@CfyV$+z&~)J|}+% zoXJNAHSN0X$A4DU3;60MU$0(IJjwZ{vMIuP zeV6%Knc(P+ykhaF!l6IMzL~_MK?iMBvzcgu{ca_l##7PKk!HWNU$;_UDs!buKK688 zBPXWp+iL?Oa=%gQ%9M^7zTQ5F0d$Pu&+{M&RVN*PzKV%c4NyKdrc^naN!-${dfW!x z9l`s<{4#4g2A*?Wx6U>XP)yQ6)&%{{b~yNyLNyl9tgj6?uP!~S)LznuT<+O_pNjf5 z(A1K(IP^sb{4s%iQ%>IZeYEnQ-CLdvoN%M>uyL2lSc9XXCZ@>FJ!TpYMG{Y zqn56+oqW8Je06Q%^Ol~b)P#57%tGUHPRQmx-tl7RWr@hQ;=Mjq)R=Y_LO5rZLhIP} zYhaptrA2H|=613i*WLLVx;eY%tkztU>U-IWNJDO+CB9Xsu%BrT<%T3*r%)=BC2{%M z^!^kpwX`E4;sA>&d+N9T->}ubCmpITRcP^f!6PM?rqbe!;cXuGkqNyV*l{5-HnjLB2Oz_abiR998wf(S{-N@HFkEx3sExDYdIVY})6fd`znIQCFJv8yC?>TY z|I*eXYGlOi!bgu%&hZ*RxbugEgbKK#sUbqmE`Y7Lm}XP_q@?xjuY;8#riDz3!pCP_ zi?)X){V7i6V71_M(lA9yWx~NQaYC4t3ZUKea$jrLv~45p>E+v8^8f*WbG23)Eu}}5 zh;PY?ysO~Pi~zCD|Dksh|$4ll1hgYCZsFTy+ z#V&j{zS9KhmsC;kyT`)*RpzXei*ndZqutap^U+9ZaN44JZ~wzg=ZGIjW%NL~orVz<_tE*A%I~I25#HS*~V*0yJvUZE@TB<))d(V0OQRxIH(z4(!y_ zNq2DEh-w2RK{<~?j$h_lgOpE3RqWnNNp*B@Hmkp$DRL6G-1qf;5C!_-1uzKX zPM(Zpeu2Ba9ciiBwS3{>kA>^nu%RahR-y;0Zcw#$fX>C^G-9j9Lo-c`B7<-6Z-9%| zhrBFzOwK&MJ*W9S<-=SeYFTAeGq0*&sLO#HoUP)KHdZ+-Olxq@9tlm_8vILa;2C9; z&3!a32gsfv-ztzDw$=TLo}=%mwWsn^-i#-VD$x_!1^8-UO$Rk6xnQwQBn~Xd~IuGJwr9;U8Db%N5{wM zjX_@*Ugd|LF3wbUyUdbVLOWz6Y>tyTG2_t2-x0IKgqyPwb5D126=aR>kHRBTg|N4Q zx^k#?tfBnFL6=#{4d-7;O&p617PM%rCW~v^Tq0}Rr0q6*nz%)c0J|YL^1|ml#?TI~ zMt2FJR=p7hV9YS>`{&;?#4rG+ewUc-vCp@Oc=9?YqIlrm?HvsDl}~H~JQWi3&)2Ci)BDKY(M*-KdoojN zpVDUAiv#bAf#eGkBS{B7Vq4qs&SGZ`eB{!x6j0-oFt2T z6!6mzJ;{^8`elm@~)=%U=NHALi~5~DMIWP#PJ`-ta-++{})htW2J3O zkyEt*Bqw74ZoRppc$7t->va~n8lx@!t{>kcK5(y&s7!!xWty$<gLleqEsZxkQyy3GLEgdYfpxKjqwukzNY*dOMsdqWa(CwR!j2Q;~y>56-P=3dx_Q zq6{nT+3<_Zy$X$C&~Z!4;@Yn>KRyY37^waGgZAfnC7OEGQ^$K=U-UA{=e;-W6!K15 z>s0=MP*uBQTZ_0pTG9TT8h=h9ahV*i{gnYsO53LNH`iFdI_u87n_3Bk_LplVxjHAZT6qT9b zI55$g7=A}9w|*b(Hn^$3MgL@dh0$J`Yrf}mPK;zw=HYC}bW@<@H7firm8>nfxH12^ zhi5tCyqbaxkK|s`JeCdGdP$c`yiF+LD24!Q1345y5S?H~izah2 zgE8%y`5YKR%@e!XQ4MFXTY7&c{I(nr2jwrtg?ABvG?FAO=|A z(D85gJFr4(nq~lu-N=@BITe z2$`F%`{ev0W6H0e8hKic`C(uXM_zwFCthbSwWVP~15keZl2|xL8HIR&B&d9ADM*wR z1`&GQMruH@GAm$K3xV0=t-p!tq|@HnKh9hS4+W@VV__MF+C5hD&p>g4V518!5m|`c zFSnFZ|K-}LX32&SO;YcVjLEX1?tQL#pU(H3AwJ}MEJ(d;|I zT#?ixb%$1lOfWO+QFzYZ>qd0v8rf#?A2U z*h1$cb_6QU)>Yy_o*f9msvg?LfS5HY|BopXGWZAgJJk=z|80i{r4Zk?n~o<%SilN3 z@h{Qc$?smyU4hEvPZvx2%Rh18Eo1C^@KSlyc#j|lSKw4UjdpuA)oTrz@DG`(X#ywe z({+zEk()G_0@RSixwZc;XT=V$}h1|t;_2FU`>`gV&_Vk&I;2}b$$PwgQG8x2aU zOC&|}Q=wOkcojYe4Mt7nRqdN_4l^!G({~SUd{#JDTNZFq_DAccJRXuAGIGj}Id zE^StLq@}Ppyk$_ztK6qeW$Sh$y4sR;D%k-y*58hzp1DLZap<95w=DqxM2(`y@aaQW59`FRhLDrC80TC z0wee$jFQqrX>wFsL=#%g+{^K*ucF%xdoDoufQLoZb`yeg>>ONdFr}MaC#tM#JTM*z zkRPgRI@S)!r6<}YEmH)liecx)BmiI#A+tLGE4J0y2ayvCXTPC9_lA*XwYo#}{{ai% B_~ZZp diff --git a/src/Files/Files.csproj b/src/Files/Files.csproj index 1893e114c71d..eb6d20071104 100644 --- a/src/Files/Files.csproj +++ b/src/Files/Files.csproj @@ -1523,10 +1523,6 @@ {0533133f-2559-4b53-a0fd-0970bc0e312e} Common - - {02ab5173-64af-488b-8d5e-87b8d16ea25c} - ICSharpCode.SharpZipLib -
From c763b3c56babdc16f0079d7251ce8e5a81523006 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sun, 28 Nov 2021 18:25:42 +0100 Subject: [PATCH 04/56] Added modified SharpZipLib nuget to project --- src/Files/Files.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Files/Files.csproj b/src/Files/Files.csproj index eb6d20071104..3c07016a5e53 100644 --- a/src/Files/Files.csproj +++ b/src/Files/Files.csproj @@ -1474,6 +1474,9 @@ 13.0.1 + + 1.3.4 + 2.0.7 From 699779a0f308a768a1915a45a43aa7e17dc27c9f Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sun, 28 Nov 2021 20:15:54 +0100 Subject: [PATCH 05/56] Fix opening empty archives --- Files.sln | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Files.sln b/Files.sln index b9fb7d1b67ab..5c66a1834839 100644 --- a/Files.sln +++ b/Files.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31911.196 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.31903.286 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.Launcher", "src\Files.Launcher\Files.Launcher.csproj", "{533F9E86-EE0A-4FCB-B70C-F29532C1B787}" ProjectSection(ProjectDependencies) = postProject From a9d1e2f82ba6e0a3aa3212590d0f85d8ad678e60 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sun, 28 Nov 2021 21:41:35 +0100 Subject: [PATCH 06/56] Add 7ZipExtractor --- Files.sln | 26 ++++++++++++++++++++++++-- src/Files/Files.csproj | 4 ++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Files.sln b/Files.sln index 5c66a1834839..66beebf2333f 100644 --- a/Files.sln +++ b/Files.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.1.31903.286 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31911.196 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.Launcher", "src\Files.Launcher\Files.Launcher.csproj", "{533F9E86-EE0A-4FCB-B70C-F29532C1B787}" ProjectSection(ProjectDependencies) = postProject @@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A74DCE98-A74 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{481DE2EA-E6CE-4A9C-A220-3B543B95AAA1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SevenZipExtractor", "..\SevenZipExtractor\SevenZipExtractor\SevenZipExtractor.csproj", "{2CAA786B-6BE4-437C-810C-758E6A3A1D8D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -193,6 +195,26 @@ Global {B7E43D1F-AC8B-4958-95D5-A06CDD1FEACD}.Release|x64.Build.0 = Release|Any CPU {B7E43D1F-AC8B-4958-95D5-A06CDD1FEACD}.Release|x86.ActiveCfg = Release|Any CPU {B7E43D1F-AC8B-4958-95D5-A06CDD1FEACD}.Release|x86.Build.0 = Release|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|ARM.ActiveCfg = Debug|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|ARM.Build.0 = Debug|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|arm64.ActiveCfg = Debug|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|arm64.Build.0 = Debug|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|x64.ActiveCfg = Debug|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|x64.Build.0 = Debug|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|x86.ActiveCfg = Debug|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|x86.Build.0 = Debug|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|Any CPU.Build.0 = Release|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|ARM.ActiveCfg = Release|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|ARM.Build.0 = Release|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|arm64.ActiveCfg = Release|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|arm64.Build.0 = Release|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|x64.ActiveCfg = Release|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|x64.Build.0 = Release|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|x86.ActiveCfg = Release|Any CPU + {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Files/Files.csproj b/src/Files/Files.csproj index 3c07016a5e53..3796b1490efe 100644 --- a/src/Files/Files.csproj +++ b/src/Files/Files.csproj @@ -1518,6 +1518,10 @@ + + {2caa786b-6be4-437c-810c-758e6a3a1d8d} + SevenZipExtractor + {3d19293f-0e3a-4946-ade3-680a1e9123ec} BackgroundTasks From 0c5b13b770ec84e8cf2bcef60276391b5180a4bc Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Mon, 29 Nov 2021 01:05:30 +0100 Subject: [PATCH 07/56] [WIP] --- Files.sln | 42 +- SevenZipWrapper/ArchiveFile.cs | 400 +++++++++++++++ SevenZipWrapper/ArchiveFileCallback.cs | 57 +++ SevenZipWrapper/ArchiveStreamCallback.cs | 45 ++ SevenZipWrapper/ArchiveStreamsCallback.cs | 58 +++ SevenZipWrapper/Formats.cs | 112 +++++ SevenZipWrapper/IArchiveExtractCallback.cs | 23 + SevenZipWrapper/Kernel32Dll.cs | 20 + SevenZipWrapper/Properties/AssemblyInfo.cs | 29 ++ .../Properties/SevenZipWrapper.rd.xml | 33 ++ SevenZipWrapper/SafeLibraryHandle.cs | 21 + SevenZipWrapper/SevenZipException.cs | 24 + SevenZipWrapper/SevenZipFormat.cs | 285 +++++++++++ SevenZipWrapper/SevenZipHandle.cs | 68 +++ SevenZipWrapper/SevenZipInterface.cs | 458 ++++++++++++++++++ SevenZipWrapper/SevenZipWrapper.csproj | 159 ++++++ SevenZipWrapper/ZipEntry.cs | 119 +++++ src/Files/Files.csproj | 11 +- .../Filesystem/StorageItems/ZipStorageFile.cs | 124 ++--- .../StorageItems/ZipStorageFolder.cs | 116 ++--- src/Files/Helpers/ZipHelpers.cs | 42 +- .../Previews/ArchivePreviewViewModel.cs | 12 +- 22 files changed, 2068 insertions(+), 190 deletions(-) create mode 100644 SevenZipWrapper/ArchiveFile.cs create mode 100644 SevenZipWrapper/ArchiveFileCallback.cs create mode 100644 SevenZipWrapper/ArchiveStreamCallback.cs create mode 100644 SevenZipWrapper/ArchiveStreamsCallback.cs create mode 100644 SevenZipWrapper/Formats.cs create mode 100644 SevenZipWrapper/IArchiveExtractCallback.cs create mode 100644 SevenZipWrapper/Kernel32Dll.cs create mode 100644 SevenZipWrapper/Properties/AssemblyInfo.cs create mode 100644 SevenZipWrapper/Properties/SevenZipWrapper.rd.xml create mode 100644 SevenZipWrapper/SafeLibraryHandle.cs create mode 100644 SevenZipWrapper/SevenZipException.cs create mode 100644 SevenZipWrapper/SevenZipFormat.cs create mode 100644 SevenZipWrapper/SevenZipHandle.cs create mode 100644 SevenZipWrapper/SevenZipInterface.cs create mode 100644 SevenZipWrapper/SevenZipWrapper.csproj create mode 100644 SevenZipWrapper/ZipEntry.cs diff --git a/Files.sln b/Files.sln index 66beebf2333f..8e0cbb1f766a 100644 --- a/Files.sln +++ b/Files.sln @@ -26,7 +26,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A74DCE98-A74 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{481DE2EA-E6CE-4A9C-A220-3B543B95AAA1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SevenZipExtractor", "..\SevenZipExtractor\SevenZipExtractor\SevenZipExtractor.csproj", "{2CAA786B-6BE4-437C-810C-758E6A3A1D8D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SevenZipWrapper", "SevenZipWrapper\SevenZipWrapper.csproj", "{FC45A0D3-7AEA-473A-B5E0-716A3814F331}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -195,26 +195,26 @@ Global {B7E43D1F-AC8B-4958-95D5-A06CDD1FEACD}.Release|x64.Build.0 = Release|Any CPU {B7E43D1F-AC8B-4958-95D5-A06CDD1FEACD}.Release|x86.ActiveCfg = Release|Any CPU {B7E43D1F-AC8B-4958-95D5-A06CDD1FEACD}.Release|x86.Build.0 = Release|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|ARM.ActiveCfg = Debug|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|ARM.Build.0 = Debug|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|arm64.ActiveCfg = Debug|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|arm64.Build.0 = Debug|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|x64.ActiveCfg = Debug|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|x64.Build.0 = Debug|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|x86.ActiveCfg = Debug|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Debug|x86.Build.0 = Debug|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|Any CPU.Build.0 = Release|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|ARM.ActiveCfg = Release|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|ARM.Build.0 = Release|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|arm64.ActiveCfg = Release|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|arm64.Build.0 = Release|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|x64.ActiveCfg = Release|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|x64.Build.0 = Release|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|x86.ActiveCfg = Release|Any CPU - {2CAA786B-6BE4-437C-810C-758E6A3A1D8D}.Release|x86.Build.0 = Release|Any CPU + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|ARM.ActiveCfg = Debug|ARM + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|ARM.Build.0 = Debug|ARM + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|arm64.ActiveCfg = Debug|ARM64 + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|arm64.Build.0 = Debug|ARM64 + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|x64.ActiveCfg = Debug|x64 + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|x64.Build.0 = Debug|x64 + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|x86.ActiveCfg = Debug|x86 + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|x86.Build.0 = Debug|x86 + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|Any CPU.Build.0 = Release|Any CPU + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|ARM.ActiveCfg = Release|ARM + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|ARM.Build.0 = Release|ARM + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|arm64.ActiveCfg = Release|ARM64 + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|arm64.Build.0 = Release|ARM64 + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|x64.ActiveCfg = Release|x64 + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|x64.Build.0 = Release|x64 + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|x86.ActiveCfg = Release|x86 + {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SevenZipWrapper/ArchiveFile.cs b/SevenZipWrapper/ArchiveFile.cs new file mode 100644 index 000000000000..6655ac43093e --- /dev/null +++ b/SevenZipWrapper/ArchiveFile.cs @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +namespace SevenZipExtractor +{ + public class ArchiveFile : IDisposable + { + private SevenZipHandle sevenZipHandle; + private readonly IInArchive archive; + private readonly InStreamWrapper archiveStream; + private IList entries; + + private string libraryFilePath; + + public ArchiveFile(string archiveFilePath, string libraryFilePath = null) + { + this.libraryFilePath = libraryFilePath; + + this.InitializeAndValidateLibrary(); + + if (!File.Exists(archiveFilePath)) + { + throw new SevenZipException("Archive file not found"); + } + + SevenZipFormat format; + string extension = Path.GetExtension(archiveFilePath); + + if (this.GuessFormatFromExtension(extension, out format)) + { + // great + } + else if (this.GuessFormatFromSignature(archiveFilePath, out format)) + { + // success + } + else + { + throw new SevenZipException(Path.GetFileName(archiveFilePath) + " is not a known archive type"); + } + + this.archive = this.sevenZipHandle.CreateInArchive(Formats.FormatGuidMapping[format]); + this.archiveStream = new InStreamWrapper(File.OpenRead(archiveFilePath)); + } + + public ArchiveFile(Stream archiveStream, SevenZipFormat? format = null, string libraryFilePath = null) + { + this.libraryFilePath = libraryFilePath; + + this.InitializeAndValidateLibrary(); + + if (archiveStream == null) + { + throw new SevenZipException("archiveStream is null"); + } + + if (format == null) + { + SevenZipFormat guessedFormat; + + if (this.GuessFormatFromSignature(archiveStream, out guessedFormat)) + { + format = guessedFormat; + } + else + { + throw new SevenZipException("Unable to guess format automatically"); + } + } + + this.archive = this.sevenZipHandle.CreateInArchive(Formats.FormatGuidMapping[format.Value]); + this.archiveStream = new InStreamWrapper(archiveStream); + } + + public bool IsStreamOwner { get; set; } = true; + + public void Extract(string outputFolder, bool overwrite = false) + { + this.Extract(entry => + { + string fileName = Path.Combine(outputFolder, entry.FileName); + + if (entry.IsFolder) + { + return fileName; + } + + if (!File.Exists(fileName) || overwrite) + { + return fileName; + } + + return null; + }); + } + + public void Extract(Func getOutputPath) + { + IList fileStreams = new List(); + + try + { + foreach (ZipEntry entry in Entries) + { + string outputPath = getOutputPath(entry); + + if (outputPath == null) // getOutputPath = null means SKIP + { + fileStreams.Add(null); + continue; + } + + if (entry.IsFolder) + { + Directory.CreateDirectory(outputPath); + fileStreams.Add(null); + continue; + } + + string directoryName = Path.GetDirectoryName(outputPath); + + if (!string.IsNullOrWhiteSpace(directoryName)) + { + Directory.CreateDirectory(directoryName); + } + + fileStreams.Add(File.Create(outputPath)); + } + + this.archive.Extract(null, 0xFFFFFFFF, 0, new ArchiveStreamsCallback(fileStreams)); + } + finally + { + foreach (Stream stream in fileStreams) + { + if (stream != null) + { + stream.Dispose(); + } + } + } + } + + public IList Entries + { + get + { + if (this.entries != null) + { + return this.entries; + } + + ulong checkPos = 32 * 1024; + int open = this.archive.Open(this.archiveStream, ref checkPos, null); + + if (open != 0) + { + throw new SevenZipException("Unable to open archive"); + } + + uint itemsCount = this.archive.GetNumberOfItems(); + + this.entries = new List(); + + for (uint fileIndex = 0; fileIndex < itemsCount; fileIndex++) + { + string fileName = this.GetProperty(fileIndex, ItemPropId.kpidPath); + bool isFolder = this.GetProperty(fileIndex, ItemPropId.kpidIsFolder); + bool isEncrypted = this.GetProperty(fileIndex, ItemPropId.kpidEncrypted); + ulong size = this.GetProperty(fileIndex, ItemPropId.kpidSize); + ulong packedSize = this.GetProperty(fileIndex, ItemPropId.kpidPackedSize); + DateTime creationTime = this.GetPropertySafe(fileIndex, ItemPropId.kpidCreationTime); + DateTime lastWriteTime = this.GetPropertySafe(fileIndex, ItemPropId.kpidLastWriteTime); + DateTime lastAccessTime = this.GetPropertySafe(fileIndex, ItemPropId.kpidLastAccessTime); + uint crc = this.GetPropertySafe(fileIndex, ItemPropId.kpidCRC); + uint attributes = this.GetPropertySafe(fileIndex, ItemPropId.kpidAttributes); + string comment = this.GetPropertySafe(fileIndex, ItemPropId.kpidComment); + string hostOS = this.GetPropertySafe(fileIndex, ItemPropId.kpidHostOS); + string method = this.GetPropertySafe(fileIndex, ItemPropId.kpidMethod); + + bool isSplitBefore = this.GetPropertySafe(fileIndex, ItemPropId.kpidSplitBefore); + bool isSplitAfter = this.GetPropertySafe(fileIndex, ItemPropId.kpidSplitAfter); + + this.entries.Add(new ZipEntry(this.archive, fileIndex) + { + FileName = fileName, + IsFolder = isFolder, + IsEncrypted = isEncrypted, + Size = size, + PackedSize = packedSize, + CreationTime = creationTime, + LastWriteTime = lastWriteTime, + LastAccessTime = lastAccessTime, + CRC = crc, + Attributes = attributes, + Comment = comment, + HostOS = hostOS, + Method = method, + IsSplitBefore = isSplitBefore, + IsSplitAfter = isSplitAfter + }); + } + + return this.entries; + } + } + + private T GetPropertySafe(uint fileIndex, ItemPropId name) + { + try + { + return this.GetProperty(fileIndex, name); + } + catch (InvalidCastException) + { + return default(T); + } + } + + private T GetProperty(uint fileIndex, ItemPropId name) + { + PropVariant propVariant = new PropVariant(); + this.archive.GetProperty(fileIndex, name, ref propVariant); + object value = propVariant.GetObject(); + + if (propVariant.VarType == VarEnum.VT_EMPTY) + { + propVariant.Clear(); + return default(T); + } + + propVariant.Clear(); + + if (value == null) + { + return default(T); + } + + Type type = typeof(T); + bool isNullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + Type underlyingType = isNullable ? Nullable.GetUnderlyingType(type) : type; + + T result = (T)Convert.ChangeType(value.ToString(), underlyingType); + + return result; + } + + private void InitializeAndValidateLibrary() + { + if (string.IsNullOrWhiteSpace(this.libraryFilePath)) + { + string currentArchitecture = IntPtr.Size == 4 ? "x86" : "x64"; // magic check + + if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "7z-" + currentArchitecture + ".dll"))) + { + this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "7z-" + currentArchitecture + ".dll"); + } + else if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", "7z-" + currentArchitecture + ".dll"))) + { + this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", "7z-" + currentArchitecture + ".dll"); + } + else if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", currentArchitecture, "7z.dll"))) + { + this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", currentArchitecture, "7z.dll"); + } + else if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, currentArchitecture, "7z.dll"))) + { + this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, currentArchitecture, "7z.dll"); + } + else if (File.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "7-Zip", "7z.dll"))) + { + this.libraryFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "7-Zip", "7z.dll"); + } + } + + if (string.IsNullOrWhiteSpace(this.libraryFilePath)) + { + throw new SevenZipException("libraryFilePath not set"); + } + + if (!File.Exists(this.libraryFilePath)) + { + throw new SevenZipException("7z.dll not found"); + } + + try + { + this.sevenZipHandle = new SevenZipHandle(this.libraryFilePath); + } + catch (Exception e) + { + throw new SevenZipException("Unable to initialize SevenZipHandle", e); + } + } + + private bool GuessFormatFromExtension(string fileExtension, out SevenZipFormat format) + { + if (string.IsNullOrWhiteSpace(fileExtension)) + { + format = SevenZipFormat.Undefined; + return false; + } + + fileExtension = fileExtension.TrimStart('.').Trim().ToLowerInvariant(); + + if (fileExtension.Equals("rar")) + { + // 7z has different GUID for Pre-RAR5 and RAR5, but they have both same extension (.rar) + // If it is [0x52 0x61 0x72 0x21 0x1A 0x07 0x01 0x00] then file is RAR5 otherwise RAR. + // https://www.rarlab.com/technote.htm + + // We are unable to guess right format just by looking at extension and have to check signature + + format = SevenZipFormat.Undefined; + return false; + } + + if (!Formats.ExtensionFormatMapping.ContainsKey(fileExtension)) + { + format = SevenZipFormat.Undefined; + return false; + } + + format = Formats.ExtensionFormatMapping[fileExtension]; + return true; + } + + + private bool GuessFormatFromSignature(string filePath, out SevenZipFormat format) + { + using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + return GuessFormatFromSignature(fileStream, out format); + } + } + + private bool GuessFormatFromSignature(Stream stream, out SevenZipFormat format) + { + int longestSignature = Formats.FileSignatures.Values.OrderByDescending(v => v.Length).First().Length; + + byte[] archiveFileSignature = new byte[longestSignature]; + int bytesRead = stream.Read(archiveFileSignature, 0, longestSignature); + + stream.Position -= bytesRead; // go back o beginning + + if (bytesRead != longestSignature) + { + format = SevenZipFormat.Undefined; + return false; + } + + foreach (KeyValuePair pair in Formats.FileSignatures) + { + if (archiveFileSignature.Take(pair.Value.Length).SequenceEqual(pair.Value)) + { + format = pair.Key; + return true; + } + } + + format = SevenZipFormat.Undefined; + return false; + } + + ~ArchiveFile() + { + this.Dispose(false); + } + + protected void Dispose(bool disposing) + { + if (IsStreamOwner) + { + if (this.archiveStream != null) + { + this.archiveStream.Dispose(); + } + } + + if (this.archive != null) + { + Marshal.ReleaseComObject(this.archive); + } + + if (this.sevenZipHandle != null) + { + this.sevenZipHandle.Dispose(); + } + } + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + } +} diff --git a/SevenZipWrapper/ArchiveFileCallback.cs b/SevenZipWrapper/ArchiveFileCallback.cs new file mode 100644 index 000000000000..e86cc6366cae --- /dev/null +++ b/SevenZipWrapper/ArchiveFileCallback.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; + +namespace SevenZipExtractor +{ + internal class ArchiveFileCallback : IArchiveExtractCallback + { + private readonly string fileName; + private readonly uint fileNumber; + private OutStreamWrapper fileStream; // to be removed + + public ArchiveFileCallback(uint fileNumber, string fileName) + { + this.fileNumber = fileNumber; + this.fileName = fileName; + } + + public void SetTotal(ulong total) + { + } + + public void SetCompleted(ref ulong completeValue) + { + } + + public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode) + { + if ((index != this.fileNumber) || (askExtractMode != AskMode.kExtract)) + { + outStream = null; + return 0; + } + + string fileDir = Path.GetDirectoryName(this.fileName); + + if (!string.IsNullOrEmpty(fileDir)) + { + Directory.CreateDirectory(fileDir); + } + + this.fileStream = new OutStreamWrapper(File.Create(this.fileName)); + + outStream = this.fileStream; + + return 0; + } + + public void PrepareOperation(AskMode askExtractMode) + { + } + + public void SetOperationResult(OperationResult resultEOperationResult) + { + this.fileStream.Dispose(); + } + } +} \ No newline at end of file diff --git a/SevenZipWrapper/ArchiveStreamCallback.cs b/SevenZipWrapper/ArchiveStreamCallback.cs new file mode 100644 index 000000000000..741e57a0abdd --- /dev/null +++ b/SevenZipWrapper/ArchiveStreamCallback.cs @@ -0,0 +1,45 @@ +using System.IO; + +namespace SevenZipExtractor +{ + internal class ArchiveStreamCallback : IArchiveExtractCallback + { + private readonly uint fileNumber; + private readonly Stream stream; + + public ArchiveStreamCallback(uint fileNumber, Stream stream) + { + this.fileNumber = fileNumber; + this.stream = stream; + } + + public void SetTotal(ulong total) + { + } + + public void SetCompleted(ref ulong completeValue) + { + } + + public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode) + { + if ((index != this.fileNumber) || (askExtractMode != AskMode.kExtract)) + { + outStream = null; + return 0; + } + + outStream = new OutStreamWrapper(this.stream); + + return 0; + } + + public void PrepareOperation(AskMode askExtractMode) + { + } + + public void SetOperationResult(OperationResult resultEOperationResult) + { + } + } +} \ No newline at end of file diff --git a/SevenZipWrapper/ArchiveStreamsCallback.cs b/SevenZipWrapper/ArchiveStreamsCallback.cs new file mode 100644 index 000000000000..b8f1fcab7334 --- /dev/null +++ b/SevenZipWrapper/ArchiveStreamsCallback.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.IO; + +namespace SevenZipExtractor +{ + internal class ArchiveStreamsCallback : IArchiveExtractCallback + { + private readonly IList streams; + + public ArchiveStreamsCallback(IList streams) + { + this.streams = streams; + } + + public void SetTotal(ulong total) + { + } + + public void SetCompleted(ref ulong completeValue) + { + } + + public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode) + { + if (askExtractMode != AskMode.kExtract) + { + outStream = null; + return 0; + } + + if (this.streams == null) + { + outStream = null; + return 0; + } + + Stream stream = this.streams[(int) index]; + + if (stream == null) + { + outStream = null; + return 0; + } + + outStream = new OutStreamWrapper(stream); + + return 0; + } + + public void PrepareOperation(AskMode askExtractMode) + { + } + + public void SetOperationResult(OperationResult resultEOperationResult) + { + } + } +} \ No newline at end of file diff --git a/SevenZipWrapper/Formats.cs b/SevenZipWrapper/Formats.cs new file mode 100644 index 000000000000..f5762bc155c6 --- /dev/null +++ b/SevenZipWrapper/Formats.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; + +namespace SevenZipExtractor +{ + public class Formats + { + internal static readonly Dictionary ExtensionFormatMapping = new Dictionary + { + {"7z", SevenZipFormat.SevenZip}, + {"gz", SevenZipFormat.GZip}, + {"tar", SevenZipFormat.Tar}, + {"rar", SevenZipFormat.Rar}, + {"zip", SevenZipFormat.Zip}, + {"lzma", SevenZipFormat.Lzma}, + {"lzh", SevenZipFormat.Lzh}, + {"arj", SevenZipFormat.Arj}, + {"bz2", SevenZipFormat.BZip2}, + {"cab", SevenZipFormat.Cab}, + {"chm", SevenZipFormat.Chm}, + {"deb", SevenZipFormat.Deb}, + {"iso", SevenZipFormat.Iso}, + {"rpm", SevenZipFormat.Rpm}, + {"wim", SevenZipFormat.Wim}, + {"udf", SevenZipFormat.Udf}, + {"mub", SevenZipFormat.Mub}, + {"xar", SevenZipFormat.Xar}, + {"hfs", SevenZipFormat.Hfs}, + {"dmg", SevenZipFormat.Dmg}, + {"z", SevenZipFormat.Lzw}, + {"xz", SevenZipFormat.XZ}, + {"flv", SevenZipFormat.Flv}, + {"swf", SevenZipFormat.Swf}, + {"exe", SevenZipFormat.PE}, + {"dll", SevenZipFormat.PE}, + {"vhd", SevenZipFormat.Vhd} + }; + + internal static Dictionary FormatGuidMapping = new Dictionary + { + {SevenZipFormat.SevenZip, new Guid("23170f69-40c1-278a-1000-000110070000")}, + {SevenZipFormat.Arj, new Guid("23170f69-40c1-278a-1000-000110040000")}, + {SevenZipFormat.BZip2, new Guid("23170f69-40c1-278a-1000-000110020000")}, + {SevenZipFormat.Cab, new Guid("23170f69-40c1-278a-1000-000110080000")}, + {SevenZipFormat.Chm, new Guid("23170f69-40c1-278a-1000-000110e90000")}, + {SevenZipFormat.Compound, new Guid("23170f69-40c1-278a-1000-000110e50000")}, + {SevenZipFormat.Cpio, new Guid("23170f69-40c1-278a-1000-000110ed0000")}, + {SevenZipFormat.Deb, new Guid("23170f69-40c1-278a-1000-000110ec0000")}, + {SevenZipFormat.GZip, new Guid("23170f69-40c1-278a-1000-000110ef0000")}, + {SevenZipFormat.Iso, new Guid("23170f69-40c1-278a-1000-000110e70000")}, + {SevenZipFormat.Lzh, new Guid("23170f69-40c1-278a-1000-000110060000")}, + {SevenZipFormat.Lzma, new Guid("23170f69-40c1-278a-1000-0001100a0000")}, + {SevenZipFormat.Nsis, new Guid("23170f69-40c1-278a-1000-000110090000")}, + {SevenZipFormat.Rar, new Guid("23170f69-40c1-278a-1000-000110030000")}, + {SevenZipFormat.Rar5, new Guid("23170f69-40c1-278a-1000-000110CC0000")}, + {SevenZipFormat.Rpm, new Guid("23170f69-40c1-278a-1000-000110eb0000")}, + {SevenZipFormat.Split, new Guid("23170f69-40c1-278a-1000-000110ea0000")}, + {SevenZipFormat.Tar, new Guid("23170f69-40c1-278a-1000-000110ee0000")}, + {SevenZipFormat.Wim, new Guid("23170f69-40c1-278a-1000-000110e60000")}, + {SevenZipFormat.Lzw, new Guid("23170f69-40c1-278a-1000-000110050000")}, + {SevenZipFormat.Zip, new Guid("23170f69-40c1-278a-1000-000110010000")}, + {SevenZipFormat.Udf, new Guid("23170f69-40c1-278a-1000-000110E00000")}, + {SevenZipFormat.Xar, new Guid("23170f69-40c1-278a-1000-000110E10000")}, + {SevenZipFormat.Mub, new Guid("23170f69-40c1-278a-1000-000110E20000")}, + {SevenZipFormat.Hfs, new Guid("23170f69-40c1-278a-1000-000110E30000")}, + {SevenZipFormat.Dmg, new Guid("23170f69-40c1-278a-1000-000110E40000")}, + {SevenZipFormat.XZ, new Guid("23170f69-40c1-278a-1000-0001100C0000")}, + {SevenZipFormat.Mslz, new Guid("23170f69-40c1-278a-1000-000110D50000")}, + {SevenZipFormat.PE, new Guid("23170f69-40c1-278a-1000-000110DD0000")}, + {SevenZipFormat.Elf, new Guid("23170f69-40c1-278a-1000-000110DE0000")}, + {SevenZipFormat.Swf, new Guid("23170f69-40c1-278a-1000-000110D70000")}, + {SevenZipFormat.Vhd, new Guid("23170f69-40c1-278a-1000-000110DC0000")}, + {SevenZipFormat.Flv, new Guid("23170f69-40c1-278a-1000-000110D60000")}, + {SevenZipFormat.SquashFS, new Guid("23170f69-40c1-278a-1000-000110D20000")}, + {SevenZipFormat.Lzma86, new Guid("23170f69-40c1-278a-1000-0001100B0000")}, + {SevenZipFormat.Ppmd, new Guid("23170f69-40c1-278a-1000-0001100D0000")}, + {SevenZipFormat.TE, new Guid("23170f69-40c1-278a-1000-000110CF0000")}, + {SevenZipFormat.UEFIc, new Guid("23170f69-40c1-278a-1000-000110D00000")}, + {SevenZipFormat.UEFIs, new Guid("23170f69-40c1-278a-1000-000110D10000")}, + {SevenZipFormat.CramFS, new Guid("23170f69-40c1-278a-1000-000110D30000")}, + {SevenZipFormat.APM, new Guid("23170f69-40c1-278a-1000-000110D40000")}, + {SevenZipFormat.Swfc, new Guid("23170f69-40c1-278a-1000-000110D80000")}, + {SevenZipFormat.Ntfs, new Guid("23170f69-40c1-278a-1000-000110D90000")}, + {SevenZipFormat.Fat, new Guid("23170f69-40c1-278a-1000-000110DA0000")}, + {SevenZipFormat.Mbr, new Guid("23170f69-40c1-278a-1000-000110DB0000")}, + {SevenZipFormat.MachO, new Guid("23170f69-40c1-278a-1000-000110DF0000")} + }; + + internal static Dictionary FileSignatures = new Dictionary + { + {SevenZipFormat.Rar5, new byte[] {0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00}}, + {SevenZipFormat.Rar, new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00 }}, + {SevenZipFormat.Vhd, new byte[] { 0x63, 0x6F, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x78 }}, + {SevenZipFormat.Deb, new byte[] { 0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E }}, + {SevenZipFormat.Dmg, new byte[] { 0x78, 0x01, 0x73, 0x0D, 0x62, 0x62, 0x60 }}, + {SevenZipFormat.SevenZip, new byte[] { 0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C }}, + {SevenZipFormat.Tar, new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72 }}, + {SevenZipFormat.Iso, new byte[] { 0x43, 0x44, 0x30, 0x30, 0x31 }}, + {SevenZipFormat.Cab, new byte[] { 0x4D, 0x53, 0x43, 0x46 }}, + {SevenZipFormat.Rpm, new byte[] { 0xed, 0xab, 0xee, 0xdb }}, + {SevenZipFormat.Xar, new byte[] { 0x78, 0x61, 0x72, 0x21 }}, + {SevenZipFormat.Chm, new byte[] { 0x49, 0x54, 0x53, 0x46 }}, + {SevenZipFormat.BZip2, new byte[] { 0x42, 0x5A, 0x68 }}, + {SevenZipFormat.Flv, new byte[] { 0x46, 0x4C, 0x56 }}, + {SevenZipFormat.Swf, new byte[] { 0x46, 0x57, 0x53 }}, + {SevenZipFormat.GZip, new byte[] { 0x1f, 0x0b }}, + {SevenZipFormat.Zip, new byte[] { 0x50, 0x4b }}, + {SevenZipFormat.Arj, new byte[] { 0x60, 0xEA }}, + {SevenZipFormat.Lzh, new byte[] { 0x2D, 0x6C, 0x68 }} + }; + } +} \ No newline at end of file diff --git a/SevenZipWrapper/IArchiveExtractCallback.cs b/SevenZipWrapper/IArchiveExtractCallback.cs new file mode 100644 index 000000000000..94938352d9f1 --- /dev/null +++ b/SevenZipWrapper/IArchiveExtractCallback.cs @@ -0,0 +1,23 @@ +using System.Runtime.InteropServices; + +namespace SevenZipExtractor +{ + [ComImport] + [Guid("23170F69-40C1-278A-0000-000600200000")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IArchiveExtractCallback //: IProgress + { + void SetTotal(ulong total); + void SetCompleted([In] ref ulong completeValue); + + [PreserveSig] + int GetStream( + uint index, + [MarshalAs(UnmanagedType.Interface)] out ISequentialOutStream outStream, + AskMode askExtractMode); + // GetStream OUT: S_OK - OK, S_FALSE - skeep this file + + void PrepareOperation(AskMode askExtractMode); + void SetOperationResult(OperationResult resultEOperationResult); + } +} \ No newline at end of file diff --git a/SevenZipWrapper/Kernel32Dll.cs b/SevenZipWrapper/Kernel32Dll.cs new file mode 100644 index 000000000000..c69a88a1c7ec --- /dev/null +++ b/SevenZipWrapper/Kernel32Dll.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.InteropServices; +using System.Security; + +namespace SevenZipExtractor +{ + internal static class Kernel32Dll + { + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + internal static extern SafeLibraryHandle LoadLibrary([MarshalAs(UnmanagedType.LPTStr)] string lpFileName); + + [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + internal static extern IntPtr GetProcAddress(SafeLibraryHandle hModule, [MarshalAs(UnmanagedType.LPStr)] string procName); + + [SuppressUnmanagedCodeSecurity] + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool FreeLibrary(IntPtr hModule); + } +} \ No newline at end of file diff --git a/SevenZipWrapper/Properties/AssemblyInfo.cs b/SevenZipWrapper/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..c17e7166c26f --- /dev/null +++ b/SevenZipWrapper/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Le informazioni generali relative a un assembly sono controllate dal seguente +// set di attributi. Modificare i valori di questi attributi per modificare le informazioni +// associate a un assembly. +[assembly: AssemblyTitle("SevenZipWrapper")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SevenZipWrapper")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Le informazioni sulla versione di un assembly sono costituite dai seguenti quattro valori: +// +// Versione principale +// Versione secondaria +// Numero di build +// Revisione +// +// È possibile specificare tutti i valori oppure impostare valori predefiniti per i numeri relativi alla revisione e alla build +// usando l'asterisco '*' come illustrato di seguito: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/SevenZipWrapper/Properties/SevenZipWrapper.rd.xml b/SevenZipWrapper/Properties/SevenZipWrapper.rd.xml new file mode 100644 index 000000000000..8e13ea57f9f9 --- /dev/null +++ b/SevenZipWrapper/Properties/SevenZipWrapper.rd.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/SevenZipWrapper/SafeLibraryHandle.cs b/SevenZipWrapper/SafeLibraryHandle.cs new file mode 100644 index 000000000000..4369cfa56597 --- /dev/null +++ b/SevenZipWrapper/SafeLibraryHandle.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.ConstrainedExecution; +using Microsoft.Win32.SafeHandles; + +namespace SevenZipExtractor +{ + internal sealed class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid + { + public SafeLibraryHandle() : base(true) + { + } + + /// Release library handle + /// true if the handle was released + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + protected override bool ReleaseHandle() + { + return Kernel32Dll.FreeLibrary(this.handle); + } + } +} \ No newline at end of file diff --git a/SevenZipWrapper/SevenZipException.cs b/SevenZipWrapper/SevenZipException.cs new file mode 100644 index 000000000000..b4f667603829 --- /dev/null +++ b/SevenZipWrapper/SevenZipException.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.Serialization; + +namespace SevenZipExtractor +{ + public class SevenZipException : Exception + { + public SevenZipException() + { + } + + public SevenZipException(string message) : base(message) + { + } + + public SevenZipException(string message, Exception innerException) : base(message, innerException) + { + } + + protected SevenZipException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/SevenZipWrapper/SevenZipFormat.cs b/SevenZipWrapper/SevenZipFormat.cs new file mode 100644 index 000000000000..c95eae54d020 --- /dev/null +++ b/SevenZipWrapper/SevenZipFormat.cs @@ -0,0 +1,285 @@ +namespace SevenZipExtractor +{ + /// + /// + /// + public enum SevenZipFormat + { + // Default invalid format value + Undefined = 0, + + /// + /// Open 7-zip archive format. + /// + /// Wikipedia information + SevenZip, + + /// + /// Proprietary Arj archive format. + /// + /// Wikipedia information + Arj, + + /// + /// Open Bzip2 archive format. + /// + /// Wikipedia information + BZip2, + + /// + /// Microsoft cabinet archive format. + /// + /// Wikipedia information + Cab, + + /// + /// Microsoft Compiled HTML Help file format. + /// + /// Wikipedia information + Chm, + + /// + /// Microsoft Compound file format. + /// + /// Wikipedia information + Compound, + + /// + /// Open Cpio archive format. + /// + /// Wikipedia information + Cpio, + + /// + /// Open Debian software package format. + /// + /// Wikipedia information + Deb, + + /// + /// Open Gzip archive format. + /// + /// Wikipedia information + GZip, + + /// + /// Open ISO disk image format. + /// + /// Wikipedia information + Iso, + + /// + /// Open Lzh archive format. + /// + /// Wikipedia information + Lzh, + + /// + /// Open core 7-zip Lzma raw archive format. + /// + /// Wikipedia information + Lzma, + + /// + /// Nullsoft installation package format. + /// + /// Wikipedia information + Nsis, + + /// + /// RarLab Rar archive format. + /// + /// Wikipedia information + Rar, + + /// + /// RarLab Rar archive format, version 5. + /// + /// Wikipedia information + Rar5, + + /// + /// Open Rpm software package format. + /// + /// Wikipedia information + Rpm, + + /// + /// Open split file format. + /// + /// Wikipedia information + Split, + + /// + /// Open Tar archive format. + /// + /// Wikipedia information + Tar, + + /// + /// Microsoft Windows Imaging disk image format. + /// + /// Wikipedia information + Wim, + + /// + /// Open LZW archive format; implemented in "compress" program; also known as "Z" archive format. + /// + /// Wikipedia information + Lzw, + + /// + /// Open Zip archive format. + /// + /// Wikipedia information + Zip, + + /// + /// Open Udf disk image format. + /// + Udf, + + /// + /// Xar open source archive format. + /// + /// Wikipedia information + Xar, + + /// + /// Mub + /// + Mub, + + /// + /// Macintosh Disk Image on CD. + /// + /// Wikipedia information + Hfs, + + /// + /// Apple Mac OS X Disk Copy Disk Image format. + /// + Dmg, + + /// + /// Open Xz archive format. + /// + /// Wikipedia information + XZ, + + /// + /// MSLZ archive format. + /// + Mslz, + + /// + /// Flash video format. + /// + /// Wikipedia information + Flv, + + /// + /// Shockwave Flash format. + /// + /// Wikipedia information + Swf, + + /// + /// Windows PE executable format. + /// + /// Wikipedia information + PE, + + /// + /// Linux executable Elf format. + /// + /// Wikipedia information + Elf, + + /// + /// Windows Installer Database. + /// + /// Wikipedia information + Msi, + + /// + /// Microsoft virtual hard disk file format. + /// + /// Wikipedia information + Vhd, + + /// + /// SquashFS file system format. + /// + /// Wikipedia information + SquashFS, + + /// + /// Lzma86 file format. + /// + Lzma86, + + /// + /// Prediction by Partial Matching by Dmitry algorithm. + /// + /// Wikipedia information + Ppmd, + + /// + /// TE format. + /// + TE, + + /// + /// UEFIc format. + /// + /// Wikipedia information + UEFIc, + + /// + /// UEFIs format. + /// + /// Wikipedia information + UEFIs, + + /// + /// Compressed ROM file system format. + /// + /// Wikipedia information + CramFS, + + /// + /// APM format. + /// + APM, + + /// + /// Swfc format. + /// + Swfc, + + /// + /// NTFS file system format. + /// + /// Wikipedia information + Ntfs, + + /// + /// FAT file system format. + /// + /// Wikipedia information + Fat, + + /// + /// MBR format. + /// + /// Wikipedia information + Mbr, + + /// + /// Mach-O file format. + /// + /// Wikipedia information + MachO + } +} \ No newline at end of file diff --git a/SevenZipWrapper/SevenZipHandle.cs b/SevenZipWrapper/SevenZipHandle.cs new file mode 100644 index 000000000000..8fa45fbcfb7a --- /dev/null +++ b/SevenZipWrapper/SevenZipHandle.cs @@ -0,0 +1,68 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace SevenZipExtractor +{ + internal class SevenZipHandle : IDisposable + { + private SafeLibraryHandle sevenZipSafeHandle; + + public SevenZipHandle(string sevenZipLibPath) + { + this.sevenZipSafeHandle = Kernel32Dll.LoadLibrary(sevenZipLibPath); + + if (this.sevenZipSafeHandle.IsInvalid) + { + throw new Win32Exception(); + } + + IntPtr functionPtr = Kernel32Dll.GetProcAddress(this.sevenZipSafeHandle, "GetHandlerProperty"); + + // Not valid dll + if (functionPtr == IntPtr.Zero) + { + this.sevenZipSafeHandle.Close(); + throw new ArgumentException(); + } + } + + ~SevenZipHandle() + { + this.Dispose(false); + } + + protected void Dispose(bool disposing) + { + if ((this.sevenZipSafeHandle != null) && !this.sevenZipSafeHandle.IsClosed) + { + this.sevenZipSafeHandle.Close(); + } + + this.sevenZipSafeHandle = null; + } + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + public IInArchive CreateInArchive(Guid classId) + { + if (this.sevenZipSafeHandle == null) + { + throw new ObjectDisposedException("SevenZipHandle"); + } + + IntPtr procAddress = Kernel32Dll.GetProcAddress(this.sevenZipSafeHandle, "CreateObject"); + CreateObjectDelegate createObject = (CreateObjectDelegate) Marshal.GetDelegateForFunctionPointer(procAddress, typeof (CreateObjectDelegate)); + + object result; + Guid interfaceId = typeof (IInArchive).GUID; + createObject(ref classId, ref interfaceId, out result); + + return result as IInArchive; + } + } +} \ No newline at end of file diff --git a/SevenZipWrapper/SevenZipInterface.cs b/SevenZipWrapper/SevenZipInterface.cs new file mode 100644 index 000000000000..7f712cd794ab --- /dev/null +++ b/SevenZipWrapper/SevenZipInterface.cs @@ -0,0 +1,458 @@ +// Version 1.5 + +using System; +using System.IO; +using System.Runtime.InteropServices; +using Windows.Foundation; + +namespace SevenZipExtractor +{ + [StructLayout(LayoutKind.Sequential)] + internal struct PropArray + { + uint length; + IntPtr pointerValues; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct PropVariant + { + [DllImport("api-ms-win-core-com-l1-1-1.dll")] + private static extern int PropVariantClear(ref PropVariant pvar); + + [DllImport("propsys.dll")] + private static extern int PropVariantToWinRTPropertyValue(ref PropVariant pvar, Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv); + + [DllImport("propsys.dll")] + private static extern string PropVariantToStringWithDefault(ref PropVariant propvarIn, [In, MarshalAs(UnmanagedType.LPWStr)] string pszDefault); + + [FieldOffset(0)] public ushort vt; + [FieldOffset(8)] public IntPtr pointerValue; + [FieldOffset(8)] public byte byteValue; + [FieldOffset(8)] public long longValue; + [FieldOffset(8)] public System.Runtime.InteropServices.ComTypes.FILETIME filetime; + [FieldOffset(8)] public PropArray propArray; + + public VarEnum VarType + { + get + { + return (VarEnum) this.vt; + } + } + + public void Clear() + { + switch (this.VarType) + { + case VarEnum.VT_EMPTY: + break; + + case VarEnum.VT_NULL: + case VarEnum.VT_I2: + case VarEnum.VT_I4: + case VarEnum.VT_R4: + case VarEnum.VT_R8: + case VarEnum.VT_CY: + case VarEnum.VT_DATE: + case VarEnum.VT_ERROR: + case VarEnum.VT_BOOL: + //case VarEnum.VT_DECIMAL: + case VarEnum.VT_I1: + case VarEnum.VT_UI1: + case VarEnum.VT_UI2: + case VarEnum.VT_UI4: + case VarEnum.VT_I8: + case VarEnum.VT_UI8: + case VarEnum.VT_INT: + case VarEnum.VT_UINT: + case VarEnum.VT_HRESULT: + case VarEnum.VT_FILETIME: + this.vt = 0; + break; + + default: + PropVariantClear(ref this); + break; + } + } + + public object GetObject() + { + switch (this.VarType) + { + case VarEnum.VT_EMPTY: + return null; + + case VarEnum.VT_FILETIME: + return DateTime.FromFileTime(this.longValue); + + //case VarEnum.VT_BSTR: + //return PropVariantToStringWithDefault(ref this, null); + + default: + PropVariantToWinRTPropertyValue(ref this, typeof(PropertyValue).GUID, out var ppv); + return null; + } + } + } + + [ComImport] + [Guid("23170F69-40C1-278A-0000-000000050000")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IProgress + { + void SetTotal(ulong total); + void SetCompleted([In] ref ulong completeValue); + } + + [ComImport] + [Guid("23170F69-40C1-278A-0000-000600100000")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IArchiveOpenCallback + { + // ref ulong replaced with IntPtr because handlers ofter pass null value + // read actual value with Marshal.ReadInt64 + void SetTotal( + IntPtr files, // [In] ref ulong files, can use 'ulong* files' but it is unsafe + IntPtr bytes); // [In] ref ulong bytes + + void SetCompleted( + IntPtr files, // [In] ref ulong files + IntPtr bytes); // [In] ref ulong bytes + } + + [ComImport] + [Guid("23170F69-40C1-278A-0000-000500100000")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface ICryptoGetTextPassword + { + [PreserveSig] + int CryptoGetTextPassword( + [MarshalAs(UnmanagedType.BStr)] out string password); + + //[return : MarshalAs(UnmanagedType.BStr)] + //string CryptoGetTextPassword(); + } + + internal enum AskMode : int + { + kExtract = 0, + kTest, + kSkip + } + + internal enum OperationResult : int + { + kOK = 0, + kUnSupportedMethod, + kDataError, + kCRCError + } + + [ComImport] + [Guid("23170F69-40C1-278A-0000-000600300000")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IArchiveOpenVolumeCallback + { + void GetProperty( + ItemPropId propID, // PROPID + IntPtr value); // PROPVARIANT + + [PreserveSig] + int GetStream( + [MarshalAs(UnmanagedType.LPWStr)] string name, + [MarshalAs(UnmanagedType.Interface)] out IInStream inStream); + } + + [ComImport] + [Guid("23170F69-40C1-278A-0000-000600400000")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IInArchiveGetStream + { + [return: MarshalAs(UnmanagedType.Interface)] + ISequentialInStream GetStream(uint index); + } + + [ComImport] + [Guid("23170F69-40C1-278A-0000-000300010000")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface ISequentialInStream + { + //[PreserveSig] + //int Read( + // [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, + // uint size, + // IntPtr processedSize); // ref uint processedSize + + uint Read( + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, + uint size); + + /* + Out: if size != 0, return_value = S_OK and (*processedSize == 0), + then there are no more bytes in stream. + if (size > 0) && there are bytes in stream, + this function must read at least 1 byte. + This function is allowed to read less than number of remaining bytes in stream. + You must call Read function in loop, if you need exact amount of data + */ + } + + [ComImport] + [Guid("23170F69-40C1-278A-0000-000300020000")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface ISequentialOutStream + { + [PreserveSig] + int Write( + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, + uint size, + IntPtr processedSize); // ref uint processedSize + /* + if (size > 0) this function must write at least 1 byte. + This function is allowed to write less than "size". + You must call Write function in loop, if you need to write exact amount of data + */ + } + + [ComImport] + [Guid("23170F69-40C1-278A-0000-000300030000")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IInStream //: ISequentialInStream + { + //[PreserveSig] + //int Read( + // [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, + // uint size, + // IntPtr processedSize); // ref uint processedSize + + uint Read( + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, + uint size); + + //[PreserveSig] + void Seek( + long offset, + uint seekOrigin, + IntPtr newPosition); // ref long newPosition + } + + [ComImport] + [Guid("23170F69-40C1-278A-0000-000300040000")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IOutStream //: ISequentialOutStream + { + [PreserveSig] + int Write( + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, + uint size, + IntPtr processedSize); // ref uint processedSize + + //[PreserveSig] + void Seek( + long offset, + uint seekOrigin, + IntPtr newPosition); // ref long newPosition + + [PreserveSig] + int SetSize(long newSize); + } + + internal enum ItemPropId : uint + { + kpidNoProperty = 0, + + kpidHandlerItemIndex = 2, + kpidPath, + kpidName, + kpidExtension, + kpidIsFolder, + kpidSize, + kpidPackedSize, + kpidAttributes, + kpidCreationTime, + kpidLastAccessTime, + kpidLastWriteTime, + kpidSolid, + kpidCommented, + kpidEncrypted, + kpidSplitBefore, + kpidSplitAfter, + kpidDictionarySize, + kpidCRC, + kpidType, + kpidIsAnti, + kpidMethod, + kpidHostOS, + kpidFileSystem, + kpidUser, + kpidGroup, + kpidBlock, + kpidComment, + kpidPosition, + kpidPrefix, + + kpidTotalSize = 0x1100, + kpidFreeSpace, + kpidClusterSize, + kpidVolumeName, + + kpidLocalName = 0x1200, + kpidProvider, + + kpidUserDefined = 0x10000 + } + + + [ComImport] + [Guid("23170F69-40C1-278A-0000-000600600000")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + //[AutomationProxy(true)] + internal interface IInArchive + { + [PreserveSig] + int Open( + IInStream stream, + /*[MarshalAs(UnmanagedType.U8)]*/ [In] ref ulong maxCheckStartPosition, + [MarshalAs(UnmanagedType.Interface)] IArchiveOpenCallback openArchiveCallback); + + void Close(); + //void GetNumberOfItems([In] ref uint numItem); + uint GetNumberOfItems(); + + void GetProperty( + uint index, + ItemPropId propID, // PROPID + ref PropVariant value); // PROPVARIANT + + [PreserveSig] + int Extract( + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] uint[] indices, //[In] ref uint indices, + uint numItems, + int testMode, + [MarshalAs(UnmanagedType.Interface)] IArchiveExtractCallback extractCallback); + + // indices must be sorted + // numItems = 0xFFFFFFFF means all files + // testMode != 0 means "test files operation" + + void GetArchiveProperty( + uint propID, // PROPID + ref PropVariant value); // PROPVARIANT + + //void GetNumberOfProperties([In] ref uint numProperties); + uint GetNumberOfProperties(); + + void GetPropertyInfo( + uint index, + [MarshalAs(UnmanagedType.BStr)] out string name, + out ItemPropId propID, // PROPID + out ushort varType); //VARTYPE + + //void GetNumberOfArchiveProperties([In] ref uint numProperties); + uint GetNumberOfArchiveProperties(); + + void GetArchivePropertyInfo( + uint index, + [MarshalAs(UnmanagedType.BStr)] string name, + ref uint propID, // PROPID + ref ushort varType); //VARTYPE + } + + internal enum ArchivePropId : uint + { + kName = 0, + kClassID, + kExtension, + kAddExtension, + kUpdate, + kKeepName, + kStartSignature, + kFinishSignature, + kAssociate + } + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int CreateObjectDelegate( + [In] ref Guid classID, + [In] ref Guid interfaceID, + //out IntPtr outObject); + [MarshalAs(UnmanagedType.Interface)] out object outObject); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int GetHandlerPropertyDelegate( + ArchivePropId propID, + ref PropVariant value); // PROPVARIANT + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int GetNumberOfFormatsDelegate(out uint numFormats); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + internal delegate int GetHandlerProperty2Delegate( + uint formatIndex, + ArchivePropId propID, + ref PropVariant value); // PROPVARIANT + + internal class StreamWrapper : IDisposable + { + protected Stream BaseStream; + + protected StreamWrapper(Stream baseStream) + { + this.BaseStream = baseStream; + } + + public void Dispose() + { + this.BaseStream.Close(); + } + + public virtual void Seek(long offset, uint seekOrigin, IntPtr newPosition) + { + long Position = (uint) this.BaseStream.Seek(offset, (SeekOrigin) seekOrigin); + + if (newPosition != IntPtr.Zero) + { + Marshal.WriteInt64(newPosition, Position); + } + } + } + + internal class InStreamWrapper : StreamWrapper, ISequentialInStream, IInStream + { + public InStreamWrapper(Stream baseStream) : base(baseStream) + { + } + + public uint Read(byte[] data, uint size) + { + return (uint) this.BaseStream.Read(data, 0, (int) size); + } + } + + internal class OutStreamWrapper : StreamWrapper, ISequentialOutStream, IOutStream + { + public OutStreamWrapper(Stream baseStream) : base(baseStream) + { + } + + public int SetSize(long newSize) + { + this.BaseStream.SetLength(newSize); + return 0; + } + + public int Write(byte[] data, uint size, IntPtr processedSize) + { + this.BaseStream.Write(data, 0, (int) size); + + if (processedSize != IntPtr.Zero) + { + Marshal.WriteInt32(processedSize, (int) size); + } + + return 0; + } + } +} \ No newline at end of file diff --git a/SevenZipWrapper/SevenZipWrapper.csproj b/SevenZipWrapper/SevenZipWrapper.csproj new file mode 100644 index 000000000000..61d53a7dfd46 --- /dev/null +++ b/SevenZipWrapper/SevenZipWrapper.csproj @@ -0,0 +1,159 @@ + + + + + Debug + AnyCPU + {FC45A0D3-7AEA-473A-B5E0-716A3814F331} + Library + Properties + SevenZipWrapper + SevenZipWrapper + it-IT + UAP + 10.0.19041.0 + 10.0.17763.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + x86 + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + x86 + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + ARM + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + ARM + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + ARM64 + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + ARM64 + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + x64 + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + x64 + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + PackageReference + + + + + + + + + + + + + + + + + + + + + 6.2.12 + + + + + + + + 14.0 + + + + \ No newline at end of file diff --git a/SevenZipWrapper/ZipEntry.cs b/SevenZipWrapper/ZipEntry.cs new file mode 100644 index 000000000000..b5d7522c6b16 --- /dev/null +++ b/SevenZipWrapper/ZipEntry.cs @@ -0,0 +1,119 @@ +using System; +using System.IO; + +namespace SevenZipExtractor +{ + public class ZipEntry + { + private readonly IInArchive archive; + private readonly uint index; + + internal ZipEntry(IInArchive archive, uint index) + { + this.archive = archive; + this.index = index; + } + + /// + /// Name of the file with its relative path within the archive + /// + public string FileName { get; internal set; } + /// + /// True if entry is a folder, false if it is a file + /// + public bool IsFolder { get; internal set; } + /// + /// Original entry size + /// + public ulong Size { get; internal set; } + /// + /// Entry size in a archived state + /// + public ulong PackedSize { get; internal set; } + + /// + /// Date and time of the file (entry) creation + /// + public DateTime CreationTime { get; internal set; } + + /// + /// Date and time of the last change of the file (entry) + /// + public DateTime LastWriteTime { get; internal set; } + + /// + /// Date and time of the last access of the file (entry) + /// + public DateTime LastAccessTime { get; internal set; } + + /// + /// CRC hash of the entry + /// + public UInt32 CRC { get; internal set; } + + /// + /// Attributes of the entry + /// + public UInt32 Attributes { get; internal set; } + + /// + /// True if entry is encrypted, otherwise false + /// + public bool IsEncrypted { get; internal set; } + + /// + /// Comment of the entry + /// + public string Comment { get; internal set; } + + /// + /// Compression method of the entry + /// + public string Method { get; internal set; } + + /// + /// Host operating system of the entry + /// + public string HostOS { get; internal set; } + + /// + /// True if there are parts of this file in previous split archive parts + /// + public bool IsSplitBefore { get; set; } + + /// + /// True if there are parts of this file in next split archive parts + /// + public bool IsSplitAfter { get; set; } + + public void Extract(string fileName, bool preserveTimestamp = true) + { + if (this.IsFolder) + { + Directory.CreateDirectory(fileName); + return; + } + + string directoryName = Path.GetDirectoryName(fileName); + + if (!string.IsNullOrWhiteSpace(directoryName)) + { + Directory.CreateDirectory(directoryName); + } + + using (FileStream fileStream = File.Create(fileName)) + { + this.Extract(fileStream); + } + + if (preserveTimestamp) + { + File.SetLastWriteTime(fileName, this.LastWriteTime); + } + } + public void Extract(Stream stream) + { + this.archive.Extract(new[] { this.index }, 1, 0, new ArchiveStreamCallback(this.index, stream)); + } + } +} diff --git a/src/Files/Files.csproj b/src/Files/Files.csproj index 3796b1490efe..3ac9d73f2304 100644 --- a/src/Files/Files.csproj +++ b/src/Files/Files.csproj @@ -1474,9 +1474,6 @@ 13.0.1 - - 1.3.4 - 2.0.7 @@ -1518,10 +1515,6 @@ - - {2caa786b-6be4-437c-810c-758e6a3a1d8d} - SevenZipExtractor - {3d19293f-0e3a-4946-ade3-680a1e9123ec} BackgroundTasks @@ -1530,6 +1523,10 @@ {0533133f-2559-4b53-a0fd-0970bc0e312e} Common + + {fc45a0d3-7aea-473a-b5e0-716a3814f331} + SevenZipWrapper + diff --git a/src/Files/Filesystem/StorageItems/ZipStorageFile.cs b/src/Files/Filesystem/StorageItems/ZipStorageFile.cs index c050b838bb8f..52a59e3374d7 100644 --- a/src/Files/Filesystem/StorageItems/ZipStorageFile.cs +++ b/src/Files/Filesystem/StorageItems/ZipStorageFile.cs @@ -1,8 +1,9 @@ using Files.Helpers; -using ICSharpCode.SharpZipLib.Zip; using Microsoft.Toolkit.Uwp; +using SevenZipExtractor; using System; using System.IO; +using System.Linq; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; using Windows.Foundation; @@ -26,7 +27,7 @@ public ZipStorageFile(string path, string containerPath, ZipEntry entry) Name = System.IO.Path.GetFileName(path.TrimEnd('\\', '/')); Path = path; ContainerPath = containerPath; - DateCreated = entry.DateTime; + DateCreated = entry.CreationTime; } public override IAsyncOperation ToStorageFileAsync() @@ -58,28 +59,36 @@ public override IAsyncOperation OpenAsync(FileAccessMode ac if (!rw) { - ZipFile zipFile = await OpenZipFileAsync(accessMode); + ArchiveFile zipFile = await OpenZipFileAsync(accessMode); if (zipFile == null) { return null; } zipFile.IsStreamOwner = true; - var znt = new ZipNameTransform(ContainerPath); - var entry = zipFile.GetEntry(znt.TransformFile(Path)); + //var znt = new ZipNameTransform(ContainerPath); + //var entry = zipFile.GetEntry(znt.TransformFile(Path)); + var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); if (entry != null) { - return new NonSeekableRandomAccessStreamForRead(zipFile.GetInputStream(entry), (ulong)entry.Size) + var ms = new MemoryStream(); + entry.Extract(ms); + return new NonSeekableRandomAccessStreamForRead(ms, (ulong)entry.Size) { - DisposeCallback = () => zipFile.Close() + DisposeCallback = () => + { + zipFile.Dispose(); + ms.Dispose(); + } }; } } else { + /* var znt = new ZipNameTransform(ContainerPath); var zipDesiredName = znt.TransformFile(Path); - using (ZipFile zipFile = await OpenZipFileAsync(accessMode)) + using (ArchiveFile zipFile = await OpenZipFileAsync(accessMode)) { var entry = zipFile.GetEntry(zipDesiredName); if (entry != null) @@ -107,6 +116,7 @@ public override IAsyncOperation OpenAsync(FileAccessMode ac await zos.PutNextEntryAsync(new ZipEntry(zipDesiredName)); return new NonSeekableRandomAccessStreamForWrite(zos); } + */ } return null; }); @@ -128,25 +138,21 @@ public override IAsyncOperation CopyAsync(IStorageFolder destin { return AsyncInfo.Run(async (cancellationToken) => { - using (ZipFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) + using (ArchiveFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) { if (zipFile == null) { return null; } zipFile.IsStreamOwner = true; - var znt = new ZipNameTransform(ContainerPath); - var entry = zipFile.GetEntry(znt.TransformFile(Path)); + //var znt = new ZipNameTransform(ContainerPath); + //var entry = zipFile.GetEntry(znt.TransformFile(Path)); + var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); if (entry != null) { var destFolder = destinationFolder.AsBaseStorageFolder(); var destFile = await destFolder.CreateFileAsync(desiredNewName, option.Convert()); - using (var inStream = zipFile.GetInputStream(entry)) - using (var outStream = await destFile.OpenStreamForWriteAsync()) - { - await inStream.CopyToAsync(outStream); - await outStream.FlushAsync(); - } + entry.Extract(await destFile.OpenStreamForWriteAsync()); return destFile; } return null; @@ -158,24 +164,20 @@ public override IAsyncAction CopyAndReplaceAsync(IStorageFile fileToReplace) { return AsyncInfo.Run(async (cancellationToken) => { - using (ZipFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) + using (ArchiveFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) { if (zipFile == null) { return; } zipFile.IsStreamOwner = true; - var znt = new ZipNameTransform(ContainerPath); - var entry = zipFile.GetEntry(znt.TransformFile(Path)); + //var znt = new ZipNameTransform(ContainerPath); + //var entry = zipFile.GetEntry(znt.TransformFile(Path)); + var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); if (entry != null) { using var hDestFile = fileToReplace.CreateSafeFileHandle(FileAccess.ReadWrite); - using (var inStream = zipFile.GetInputStream(entry)) - using (var outStream = new FileStream(hDestFile, FileAccess.Write)) - { - await inStream.CopyToAsync(outStream); - await outStream.FlushAsync(); - } + entry.Extract(new FileStream(hDestFile, FileAccess.Write)); } } }); @@ -265,19 +267,26 @@ public override IAsyncOperation OpenReadAsyn } } - ZipFile zipFile = await OpenZipFileAsync(FileAccessMode.Read); + ArchiveFile zipFile = await OpenZipFileAsync(FileAccessMode.Read); if (zipFile == null) { return null; } zipFile.IsStreamOwner = true; - var znt = new ZipNameTransform(ContainerPath); - var entry = zipFile.GetEntry(znt.TransformFile(Path)); + //var znt = new ZipNameTransform(ContainerPath); + //var entry = zipFile.GetEntry(znt.TransformFile(Path)); + var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); if (entry != null) { - var nsStream = new NonSeekableRandomAccessStreamForRead(zipFile.GetInputStream(entry), (ulong)entry.Size) + var ms = new MemoryStream(); + entry.Extract(ms); + var nsStream = new NonSeekableRandomAccessStreamForRead(ms, (ulong)entry.Size) { - DisposeCallback = () => zipFile.Close() + DisposeCallback = () => + { + zipFile.Dispose(); + ms.Dispose(); + } }; return new StreamWithContentType(nsStream); } @@ -306,17 +315,27 @@ public override IAsyncOperation OpenSequentialReadAsync() } } - ZipFile zipFile = await OpenZipFileAsync(FileAccessMode.Read); + ArchiveFile zipFile = await OpenZipFileAsync(FileAccessMode.Read); if (zipFile == null) { return null; } zipFile.IsStreamOwner = true; - var znt = new ZipNameTransform(ContainerPath); - var entry = zipFile.GetEntry(znt.TransformFile(Path)); + //var znt = new ZipNameTransform(ContainerPath); + //var entry = zipFile.GetEntry(znt.TransformFile(Path)); + var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); if (entry != null) { - return new InputStreamWithDisposeCallback(zipFile.GetInputStream(entry)) { DisposeCallback = () => zipFile.Close() }; + var ms = new MemoryStream(); + entry.Extract(ms); + return new InputStreamWithDisposeCallback(ms) + { + DisposeCallback = () => + { + zipFile.Dispose(); + ms.Dispose(); + } + }; } return null; }); @@ -390,14 +409,14 @@ public override string DisplayType #region Private - private IAsyncOperation OpenZipFileAsync(FileAccessMode accessMode, bool openProtected = false) + private IAsyncOperation OpenZipFileAsync(FileAccessMode accessMode, bool openProtected = false) { - return AsyncInfo.Run(async (cancellationToken) => + return AsyncInfo.Run(async (cancellationToken) => { bool readWrite = accessMode == FileAccessMode.ReadWrite; if (BackingFile != null) { - return new ZipFile((await BackingFile.OpenAsync(accessMode)).AsStream()); + return new ArchiveFile((await BackingFile.OpenAsync(accessMode)).AsStream()); } else { @@ -408,7 +427,7 @@ await NativeFileOperationsHelper.OpenProtectedFileForRead(ContainerPath) : { return null; } - return new ZipFile((Stream)new FileStream(hFile, readWrite ? FileAccess.ReadWrite : FileAccess.Read)); + return new ArchiveFile((Stream)new FileStream(hFile, readWrite ? FileAccess.ReadWrite : FileAccess.Read)); } }); } @@ -421,7 +440,7 @@ private StreamedFileDataRequestedHandler ZipDataStreamingHandler(string name) { // If called from here it fails with Access Denied?! //var hFile = NativeFileOperationsHelper.OpenFileForRead(ContainerPath); - using (ZipFile zipFile = await OpenZipFileAsync(FileAccessMode.Read, openProtected: true)) + using (ArchiveFile zipFile = await OpenZipFileAsync(FileAccessMode.Read, openProtected: true)) { if (zipFile == null) { @@ -429,16 +448,12 @@ private StreamedFileDataRequestedHandler ZipDataStreamingHandler(string name) return; } zipFile.IsStreamOwner = true; - var znt = new ZipNameTransform(ContainerPath); - var entry = zipFile.GetEntry(znt.TransformFile(name)); + //var znt = new ZipNameTransform(ContainerPath); + //var entry = zipFile.GetEntry(znt.TransformFile(name)); + var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == name); if (entry != null) { - using (var inStream = zipFile.GetInputStream(entry)) - using (var outStream = request.AsStreamForWrite()) - { - await inStream.CopyToAsync(outStream); - await outStream.FlushAsync(); - } + entry.Extract(request.AsStreamForWrite()); request.Dispose(); } else @@ -456,15 +471,16 @@ private StreamedFileDataRequestedHandler ZipDataStreamingHandler(string name) private async Task GetBasicProperties() { - using (ZipFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) + using (ArchiveFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) { if (zipFile == null) { return null; } zipFile.IsStreamOwner = true; - var znt = new ZipNameTransform(ContainerPath); - var entry = zipFile.GetEntry(znt.TransformFile(Path)); + //var znt = new ZipNameTransform(ContainerPath); + //var entry = zipFile.GetEntry(znt.TransformFile(Path)); + var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); if (entry != null) { return new ZipFileBasicProperties(entry); @@ -482,7 +498,7 @@ private static bool CheckAccess(string path) { return false; } - using (ZipFile zipFile = new ZipFile(new FileStream(hFile, FileAccess.Read))) + using (ArchiveFile zipFile = new ArchiveFile(new FileStream(hFile, FileAccess.Read))) { zipFile.IsStreamOwner = true; } @@ -503,9 +519,9 @@ public ZipFileBasicProperties(ZipEntry entry) this.zipEntry = entry; } - public override DateTimeOffset DateModified => zipEntry.DateTime; + public override DateTimeOffset DateModified => zipEntry.CreationTime; - public override DateTimeOffset ItemDate => zipEntry.DateTime; + public override DateTimeOffset ItemDate => zipEntry.CreationTime; public override ulong Size => (ulong)zipEntry.Size; } diff --git a/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs b/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs index 74cfbe9f9735..fb3752c9dbb7 100644 --- a/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs +++ b/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs @@ -1,7 +1,7 @@ using Files.Extensions; using Files.Helpers; -using ICSharpCode.SharpZipLib.Zip; using Microsoft.Toolkit.Uwp; +using SevenZipExtractor; using System; using System.Collections.Generic; using System.IO; @@ -37,55 +37,15 @@ public static async Task CheckDefaultZipApp(string filePath) return IsDefaultZipApp ?? await queryFileAssoc(); } - static ZipStorageFolder() - { - // Register all supported codepages (default is UTF-X only) - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - // Use extended ascii so you can convert the string back to bytes - ZipStrings.CodePage = Constants.Filesystem.ExtendedAsciiCodePage; - } - public static string DecodeEntryName(ZipEntry entry, Encoding zipEncoding) { - if (zipEncoding == null || entry.IsUnicodeText) - { - return entry.Name; - } - var decoded = Common.Extensions.IgnoreExceptions(() => - { - var rawBytes = Encoding.GetEncoding(Constants.Filesystem.ExtendedAsciiCodePage).GetBytes(entry.Name); - return zipEncoding.GetString(rawBytes); - }); - return decoded ?? entry.Name; + // TODO + return entry.FileName; } - public static Encoding DetectFileEncoding(ZipFile zipFile) + public static Encoding DetectFileEncoding(ArchiveFile zipFile) { - long readEntries = 0; - Ude.CharsetDetector cdet = new Ude.CharsetDetector(); - foreach (var entry in zipFile.OfType()) - { - readEntries++; - if (entry.IsUnicodeText) - { - return Encoding.UTF8; - } - var guessedEncoding = Common.Extensions.IgnoreExceptions(() => - { - var rawBytes = Encoding.GetEncoding(Constants.Filesystem.ExtendedAsciiCodePage).GetBytes(entry.Name); - cdet.Feed(rawBytes, 0, rawBytes.Length); - cdet.DataEnd(); - if (cdet.Charset != null && cdet.Confidence >= 0.9 && (readEntries >= Math.Min(zipFile.Count, 50))) - { - return Encoding.GetEncoding(cdet.Charset); - } - return null; - }); - if (guessedEncoding != null) - { - return guessedEncoding; - } - } + // TODO return Encoding.UTF8; } @@ -101,7 +61,7 @@ public ZipStorageFolder(string path, string containerPath, ZipEntry entry) Name = System.IO.Path.GetFileName(path.TrimEnd('\\', '/')); Path = path; ContainerPath = containerPath; - DateCreated = entry.DateTime; + DateCreated = entry.CreationTime; } public ZipStorageFolder(BaseStorageFile backingFile) @@ -125,8 +85,10 @@ public override IAsyncOperation CreateFileAsync(string desiredN { return AsyncInfo.Run(async (cancellationToken) => { - using (ZipFile zipFile = await OpenZipFileAsync(FileAccessMode.ReadWrite)) + using (ArchiveFile zipFile = await OpenZipFileAsync(FileAccessMode.ReadWrite)) { + return null; + /* if (zipFile == null) { return null; @@ -135,14 +97,12 @@ public override IAsyncOperation CreateFileAsync(string desiredN var znt = new ZipNameTransform(ContainerPath); var zipDesiredName = znt.TransformFile(System.IO.Path.Combine(Path, desiredName)); - var entry = zipFile.GetEntry(zipDesiredName); + var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == zipDesiredName); - zipFile.BeginUpdate(new MemoryArchiveStorage(FileUpdateMode.Direct)); if (entry != null) { if (options != CreationCollisionOption.ReplaceExisting) { - zipFile.AbortUpdate(); return null; } zipFile.Delete(entry); @@ -152,6 +112,7 @@ public override IAsyncOperation CreateFileAsync(string desiredN var wnt = new WindowsNameTransform(ContainerPath); return new ZipStorageFile(wnt.TransformFile(zipDesiredName), ContainerPath) { BackingFile = BackingFile }; + */ } }); } @@ -165,8 +126,9 @@ public override IAsyncOperation CreateFolderAsync(string desi { return AsyncInfo.Run(async (cancellationToken) => { - using (ZipFile zipFile = await OpenZipFileAsync(FileAccessMode.ReadWrite)) + using (ArchiveFile zipFile = await OpenZipFileAsync(FileAccessMode.ReadWrite)) { + /* if (zipFile == null) { return null; @@ -196,6 +158,8 @@ public override IAsyncOperation CreateFolderAsync(string desi ZipEncoding = ZipEncoding, BackingFile = BackingFile }; + */ + return null; } }); } @@ -220,7 +184,7 @@ public override IAsyncOperation GetItemAsync(string name) { return AsyncInfo.Run(async (cancellationToken) => { - using (ZipFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) + using (ArchiveFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) { if (zipFile == null) { @@ -228,13 +192,14 @@ public override IAsyncOperation GetItemAsync(string name) } zipFile.IsStreamOwner = true; ZipEncoding ??= DetectFileEncoding(zipFile); - var entry = zipFile.GetEntry(name); + var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == name); if (entry != null) { - var wnt = new WindowsNameTransform(ContainerPath); - if (entry.IsDirectory) + //var wnt = new WindowsNameTransform(ContainerPath); + if (entry.IsFolder) { - return new ZipStorageFolder(wnt.TransformDirectory(DecodeEntryName(entry, ZipEncoding)), ContainerPath, entry) + //return new ZipStorageFolder(wnt.TransformDirectory(DecodeEntryName(entry, ZipEncoding)), ContainerPath, entry) + return new ZipStorageFolder(DecodeEntryName(entry, ZipEncoding), ContainerPath, entry) { ZipEncoding = ZipEncoding, BackingFile = BackingFile @@ -242,7 +207,8 @@ public override IAsyncOperation GetItemAsync(string name) } else { - return new ZipStorageFile(wnt.TransformFile(DecodeEntryName(entry, ZipEncoding)), ContainerPath, entry) { BackingFile = BackingFile }; + //return new ZipStorageFile(wnt.TransformFile(DecodeEntryName(entry, ZipEncoding)), ContainerPath, entry) { BackingFile = BackingFile }; + return new ZipStorageFile(DecodeEntryName(entry, ZipEncoding), ContainerPath, entry) { BackingFile = BackingFile }; } } return null; @@ -270,7 +236,7 @@ public override IAsyncOperation> GetItemsAsync() { return AsyncInfo.Run>(async (cancellationToken) => { - using (ZipFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) + using (ArchiveFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) { if (zipFile == null) { @@ -278,17 +244,18 @@ public override IAsyncOperation> GetItemsAsync() } zipFile.IsStreamOwner = true; ZipEncoding ??= DetectFileEncoding(zipFile); - var wnt = new WindowsNameTransform(ContainerPath, true); // Check with Path.GetFullPath + //var wnt = new WindowsNameTransform(ContainerPath, true); // Check with Path.GetFullPath var items = new List(); - foreach (var entry in zipFile.OfType()) // Returns all items recursively + foreach (var entry in zipFile.Entries) // Returns all items recursively { - string winPath = System.IO.Path.GetFullPath(entry.IsDirectory ? wnt.TransformDirectory(DecodeEntryName(entry, ZipEncoding)) : wnt.TransformFile(DecodeEntryName(entry, ZipEncoding))); + string winPath = System.IO.Path.GetFullPath(entry.IsFolder ? DecodeEntryName(entry, ZipEncoding) : DecodeEntryName(entry, ZipEncoding)); + //string winPath = System.IO.Path.GetFullPath(entry.IsDirectory ? wnt.TransformDirectory(DecodeEntryName(entry, ZipEncoding)) : wnt.TransformFile(DecodeEntryName(entry, ZipEncoding))); if (winPath.StartsWith(Path.WithEnding("\\"), StringComparison.Ordinal)) // Child of self { var split = winPath.Substring(Path.Length).Split('\\', StringSplitOptions.RemoveEmptyEntries); if (split.Length > 0) { - if (entry.IsDirectory || split.Length > 1) // Not all folders have a ZipEntry + if (entry.IsFolder || split.Length > 1) // Not all folders have a ZipEntry { var itemPath = System.IO.Path.Combine(Path, split[0]); if (!items.Any(x => x.Path == itemPath)) @@ -564,14 +531,14 @@ public override string DisplayType #region Private - private IAsyncOperation OpenZipFileAsync(FileAccessMode accessMode) + private IAsyncOperation OpenZipFileAsync(FileAccessMode accessMode) { - return AsyncInfo.Run(async (cancellationToken) => + return AsyncInfo.Run(async (cancellationToken) => { bool readWrite = accessMode == FileAccessMode.ReadWrite; if (BackingFile != null) { - return new ZipFile((await BackingFile.OpenAsync(accessMode)).AsStream()); + return new ArchiveFile((await BackingFile.OpenAsync(accessMode)).AsStream()); } else { @@ -580,7 +547,7 @@ private IAsyncOperation OpenZipFileAsync(FileAccessMode accessMode) { return null; } - return new ZipFile((Stream)new FileStream(hFile, readWrite ? FileAccess.ReadWrite : FileAccess.Read)); + return new ArchiveFile((Stream)new FileStream(hFile, readWrite ? FileAccess.ReadWrite : FileAccess.Read)); } }); } @@ -616,7 +583,7 @@ private static bool CheckAccess(Stream stream) { return Common.Extensions.IgnoreExceptions(() => { - using (ZipFile zipFile = new ZipFile(stream)) + using (ArchiveFile zipFile = new ArchiveFile(stream)) { zipFile.IsStreamOwner = false; } @@ -626,15 +593,16 @@ private static bool CheckAccess(Stream stream) private async Task GetBasicProperties() { - using (ZipFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) + using (ArchiveFile zipFile = await OpenZipFileAsync(FileAccessMode.Read)) { if (zipFile == null) { return new BaseBasicProperties(); } zipFile.IsStreamOwner = true; - var znt = new ZipNameTransform(ContainerPath); - var entry = zipFile.GetEntry(znt.TransformFile(Path)); + //var znt = new ZipNameTransform(ContainerPath); + //var entry = zipFile.GetEntry(znt.TransformFile(Path)); + var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); if (entry != null) { return new ZipFolderBasicProperties(entry); @@ -643,12 +611,12 @@ private async Task GetBasicProperties() } } - private class FileDataSource : IStaticDataSource + /*private class FileDataSource : IStaticDataSource { public Stream Stream { get; set; } public Stream GetSource() => Stream; - } + }*/ private class ZipFolderBasicProperties : BaseBasicProperties { @@ -659,9 +627,9 @@ public ZipFolderBasicProperties(ZipEntry entry) this.zipEntry = entry; } - public override DateTimeOffset DateModified => zipEntry.DateTime; + public override DateTimeOffset DateModified => zipEntry.CreationTime; - public override DateTimeOffset ItemDate => zipEntry.DateTime; + public override DateTimeOffset ItemDate => zipEntry.CreationTime; public override ulong Size => (ulong)zipEntry.Size; } diff --git a/src/Files/Helpers/ZipHelpers.cs b/src/Files/Helpers/ZipHelpers.cs index cb4b2125e80f..64289e28b4c9 100644 --- a/src/Files/Helpers/ZipHelpers.cs +++ b/src/Files/Helpers/ZipHelpers.cs @@ -1,6 +1,5 @@ using Files.Filesystem.StorageItems; -using ICSharpCode.SharpZipLib.Core; -using ICSharpCode.SharpZipLib.Zip; +using SevenZipExtractor; using System; using System.Collections.Generic; using System.IO; @@ -14,7 +13,7 @@ public static class ZipHelpers { public static async Task ExtractArchive(BaseStorageFile archive, BaseStorageFolder destinationFolder, IProgress progressDelegate, CancellationToken cancellationToken) { - ZipFile zipFile = await Filesystem.FilesystemTasks.Wrap(async () => new ZipFile(await archive.OpenStreamForReadAsync())); + ArchiveFile zipFile = await Filesystem.FilesystemTasks.Wrap(async () => new ArchiveFile(await archive.OpenStreamForReadAsync())); if (zipFile == null) { return; @@ -24,9 +23,9 @@ public static async Task ExtractArchive(BaseStorageFile archive, BaseStorageFold zipFile.IsStreamOwner = true; List directoryEntries = new List(); List fileEntries = new List(); - foreach (ZipEntry entry in zipFile) + foreach (ZipEntry entry in zipFile.Entries) { - if (entry.IsFile) + if (!entry.IsFolder) { fileEntries.Add(entry); } @@ -41,20 +40,20 @@ public static async Task ExtractArchive(BaseStorageFile archive, BaseStorageFold return; } - var wnt = new WindowsNameTransform(destinationFolder.Path); + //var wnt = new WindowsNameTransform(destinationFolder.Path); var zipEncoding = ZipStorageFolder.DetectFileEncoding(zipFile); var directories = new List(); try { - directories.AddRange(directoryEntries.Select((entry) => wnt.TransformDirectory(ZipStorageFolder.DecodeEntryName(entry, zipEncoding)))); - directories.AddRange(fileEntries.Select((entry) => Path.GetDirectoryName(wnt.TransformFile(ZipStorageFolder.DecodeEntryName(entry, zipEncoding))))); + directories.AddRange(directoryEntries.Select((entry) => ZipStorageFolder.DecodeEntryName(entry, zipEncoding))); + directories.AddRange(fileEntries.Select((entry) => Path.GetDirectoryName(ZipStorageFolder.DecodeEntryName(entry, zipEncoding)))); } - catch (InvalidNameException ex) + catch (Exception ex) { App.Logger.Warn(ex, $"Error transforming zip names into: {destinationFolder.Path}\n" + - $"Directories: {string.Join(", ", directoryEntries.Select(x => x.Name))}\n" + - $"Files: {string.Join(", ", fileEntries.Select(x => x.Name))}"); + $"Directories: {string.Join(", ", directoryEntries.Select(x => x.FileName))}\n" + + $"Files: {string.Join(", ", fileEntries.Select(x => x.FileName))}"); return; } @@ -93,13 +92,13 @@ public static async Task ExtractArchive(BaseStorageFile archive, BaseStorageFold { return; } - if (entry.IsCrypted) + if (entry.IsEncrypted) { - App.Logger.Info($"Skipped encrypted zip entry: {entry.Name}"); + App.Logger.Info($"Skipped encrypted zip entry: {entry.FileName}"); continue; // TODO: support password protected archives } - string filePath = wnt.TransformFile(ZipStorageFolder.DecodeEntryName(entry, zipEncoding)); + string filePath = ZipStorageFolder.DecodeEntryName(entry, zipEncoding); var hFile = NativeFileOperationsHelper.CreateFileForWrite(filePath); if (hFile.IsInvalid) @@ -110,22 +109,9 @@ public static async Task ExtractArchive(BaseStorageFile archive, BaseStorageFold // We don't close hFile because FileStream.Dispose() already does that using (FileStream destinationStream = new FileStream(hFile, FileAccess.Write)) { - int currentBlockSize = 0; - try { - using (Stream entryStream = zipFile.GetInputStream(entry)) - { - while ((currentBlockSize = await entryStream.ReadAsync(buffer, 0, buffer.Length)) > 0) - { - await destinationStream.WriteAsync(buffer, 0, currentBlockSize); - - if (cancellationToken.IsCancellationRequested) // Check if cancelled - { - return; - } - } - } + entry.Extract(destinationStream); } catch (Exception ex) { diff --git a/src/Files/ViewModels/Previews/ArchivePreviewViewModel.cs b/src/Files/ViewModels/Previews/ArchivePreviewViewModel.cs index 5c1d791323fd..b4f8dc3cedf7 100644 --- a/src/Files/ViewModels/Previews/ArchivePreviewViewModel.cs +++ b/src/Files/ViewModels/Previews/ArchivePreviewViewModel.cs @@ -2,7 +2,7 @@ using Files.Extensions; using Files.Filesystem; using Files.ViewModels.Properties; -using ICSharpCode.SharpZipLib.Zip; +using SevenZipExtractor; using Microsoft.Toolkit.Uwp; using System.Collections.Generic; using System.IO; @@ -24,16 +24,16 @@ public ArchivePreviewViewModel(ListedItem item) : base(item) public override async Task> LoadPreviewAndDetails() { var details = new List(); - using ZipFile zipFile = new ZipFile(await Item.ItemFile.OpenStreamForReadAsync()); + using ArchiveFile zipFile = new ArchiveFile(await Item.ItemFile.OpenStreamForReadAsync()); zipFile.IsStreamOwner = true; var folderCount = 0; var fileCount = 0; - long totalSize = 0; + ulong totalSize = 0; - foreach (ZipEntry entry in zipFile) + foreach (ZipEntry entry in zipFile.Entries) { - if (entry.IsFile) + if (!entry.IsFolder) { fileCount++; totalSize += entry.Size; @@ -47,7 +47,7 @@ public override async Task> LoadPreviewAndDetails() details.Add(new FileProperty() { NameResource = "PropertyItemCount", - Value = string.Format("DetailsArchiveItemCount".GetLocalized(), zipFile.Count, fileCount, folderCount), + Value = string.Format("DetailsArchiveItemCount".GetLocalized(), zipFile.Entries.Count, fileCount, folderCount), }); details.Add(new FileProperty() From fb9fd04df3094a58ce0af8d7208c72072eaf677d Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Mon, 29 Nov 2021 02:20:52 +0100 Subject: [PATCH 08/56] Restored browsing zips --- SevenZipWrapper/ArchiveFile.cs | 20 +------- SevenZipWrapper/SevenZipInterface.cs | 7 +-- .../Filesystem/StorageItems/ZipStorageFile.cs | 43 ++++++----------- .../StorageItems/ZipStorageFolder.cs | 46 ++++++------------- 4 files changed, 31 insertions(+), 85 deletions(-) diff --git a/SevenZipWrapper/ArchiveFile.cs b/SevenZipWrapper/ArchiveFile.cs index 6655ac43093e..c340e37f0ae5 100644 --- a/SevenZipWrapper/ArchiveFile.cs +++ b/SevenZipWrapper/ArchiveFile.cs @@ -254,25 +254,9 @@ private void InitializeAndValidateLibrary() { string currentArchitecture = IntPtr.Size == 4 ? "x86" : "x64"; // magic check - if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "7z-" + currentArchitecture + ".dll"))) + if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SevenZipWrapper", currentArchitecture, "7z.dll"))) { - this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "7z-" + currentArchitecture + ".dll"); - } - else if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", "7z-" + currentArchitecture + ".dll"))) - { - this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", "7z-" + currentArchitecture + ".dll"); - } - else if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", currentArchitecture, "7z.dll"))) - { - this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", currentArchitecture, "7z.dll"); - } - else if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, currentArchitecture, "7z.dll"))) - { - this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, currentArchitecture, "7z.dll"); - } - else if (File.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "7-Zip", "7z.dll"))) - { - this.libraryFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "7-Zip", "7z.dll"); + this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SevenZipWrapper", currentArchitecture, "7z.dll"); } } diff --git a/SevenZipWrapper/SevenZipInterface.cs b/SevenZipWrapper/SevenZipInterface.cs index 7f712cd794ab..36d482981278 100644 --- a/SevenZipWrapper/SevenZipInterface.cs +++ b/SevenZipWrapper/SevenZipInterface.cs @@ -87,12 +87,9 @@ public object GetObject() case VarEnum.VT_FILETIME: return DateTime.FromFileTime(this.longValue); - //case VarEnum.VT_BSTR: - //return PropVariantToStringWithDefault(ref this, null); - default: - PropVariantToWinRTPropertyValue(ref this, typeof(PropertyValue).GUID, out var ppv); - return null; + PropVariantToWinRTPropertyValue(ref this, typeof(IPropertyValue).GUID, out var ppv); + return ppv; } } } diff --git a/src/Files/Filesystem/StorageItems/ZipStorageFile.cs b/src/Files/Filesystem/StorageItems/ZipStorageFile.cs index 52a59e3374d7..9afb87ec92d1 100644 --- a/src/Files/Filesystem/StorageItems/ZipStorageFile.cs +++ b/src/Files/Filesystem/StorageItems/ZipStorageFile.cs @@ -18,16 +18,16 @@ public sealed class ZipStorageFile : BaseStorageFile public ZipStorageFile(string path, string containerPath) { Name = System.IO.Path.GetFileName(path.TrimEnd('\\', '/')); - Path = path; + Path = System.IO.Path.Combine(containerPath, path); ContainerPath = containerPath; } public ZipStorageFile(string path, string containerPath, ZipEntry entry) { Name = System.IO.Path.GetFileName(path.TrimEnd('\\', '/')); - Path = path; + Path = System.IO.Path.Combine(containerPath, path); ContainerPath = containerPath; - DateCreated = entry.CreationTime; + DateCreated = entry.CreationTime == DateTime.MinValue ? DateTimeOffset.MinValue : entry.CreationTime; } public override IAsyncOperation ToStorageFileAsync() @@ -65,9 +65,7 @@ public override IAsyncOperation OpenAsync(FileAccessMode ac return null; } zipFile.IsStreamOwner = true; - //var znt = new ZipNameTransform(ContainerPath); - //var entry = zipFile.GetEntry(znt.TransformFile(Path)); - var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == Path); if (entry != null) { var ms = new MemoryStream(); @@ -85,12 +83,9 @@ public override IAsyncOperation OpenAsync(FileAccessMode ac else { /* - var znt = new ZipNameTransform(ContainerPath); - var zipDesiredName = znt.TransformFile(Path); - using (ArchiveFile zipFile = await OpenZipFileAsync(accessMode)) { - var entry = zipFile.GetEntry(zipDesiredName); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == Path); if (entry != null) { zipFile.BeginUpdate(new MemoryArchiveStorage(FileUpdateMode.Direct)); @@ -145,9 +140,7 @@ public override IAsyncOperation CopyAsync(IStorageFolder destin return null; } zipFile.IsStreamOwner = true; - //var znt = new ZipNameTransform(ContainerPath); - //var entry = zipFile.GetEntry(znt.TransformFile(Path)); - var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == Path); if (entry != null) { var destFolder = destinationFolder.AsBaseStorageFolder(); @@ -171,9 +164,7 @@ public override IAsyncAction CopyAndReplaceAsync(IStorageFile fileToReplace) return; } zipFile.IsStreamOwner = true; - //var znt = new ZipNameTransform(ContainerPath); - //var entry = zipFile.GetEntry(znt.TransformFile(Path)); - var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == Path); if (entry != null) { using var hDestFile = fileToReplace.CreateSafeFileHandle(FileAccess.ReadWrite); @@ -273,9 +264,7 @@ public override IAsyncOperation OpenReadAsyn return null; } zipFile.IsStreamOwner = true; - //var znt = new ZipNameTransform(ContainerPath); - //var entry = zipFile.GetEntry(znt.TransformFile(Path)); - var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == Path); if (entry != null) { var ms = new MemoryStream(); @@ -321,9 +310,7 @@ public override IAsyncOperation OpenSequentialReadAsync() return null; } zipFile.IsStreamOwner = true; - //var znt = new ZipNameTransform(ContainerPath); - //var entry = zipFile.GetEntry(znt.TransformFile(Path)); - var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == Path); if (entry != null) { var ms = new MemoryStream(); @@ -448,9 +435,7 @@ private StreamedFileDataRequestedHandler ZipDataStreamingHandler(string name) return; } zipFile.IsStreamOwner = true; - //var znt = new ZipNameTransform(ContainerPath); - //var entry = zipFile.GetEntry(znt.TransformFile(name)); - var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == name); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == name); if (entry != null) { entry.Extract(request.AsStreamForWrite()); @@ -478,9 +463,7 @@ private async Task GetBasicProperties() return null; } zipFile.IsStreamOwner = true; - //var znt = new ZipNameTransform(ContainerPath); - //var entry = zipFile.GetEntry(znt.TransformFile(Path)); - var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == Path); if (entry != null) { return new ZipFileBasicProperties(entry); @@ -519,9 +502,9 @@ public ZipFileBasicProperties(ZipEntry entry) this.zipEntry = entry; } - public override DateTimeOffset DateModified => zipEntry.CreationTime; + public override DateTimeOffset DateModified => zipEntry.CreationTime == DateTime.MinValue ? DateTimeOffset.MinValue : zipEntry.CreationTime; - public override DateTimeOffset ItemDate => zipEntry.CreationTime; + public override DateTimeOffset ItemDate => zipEntry.CreationTime == DateTime.MinValue ? DateTimeOffset.MinValue : zipEntry.CreationTime; public override ulong Size => (ulong)zipEntry.Size; } diff --git a/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs b/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs index fb3752c9dbb7..4b83edba07d8 100644 --- a/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs +++ b/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs @@ -52,16 +52,16 @@ public static Encoding DetectFileEncoding(ArchiveFile zipFile) public ZipStorageFolder(string path, string containerPath) { Name = System.IO.Path.GetFileName(path.TrimEnd('\\', '/')); - Path = path; + Path = System.IO.Path.Combine(containerPath, path); ContainerPath = containerPath; } public ZipStorageFolder(string path, string containerPath, ZipEntry entry) { Name = System.IO.Path.GetFileName(path.TrimEnd('\\', '/')); - Path = path; + Path = System.IO.Path.Combine(containerPath, path); ContainerPath = containerPath; - DateCreated = entry.CreationTime; + DateCreated = entry.CreationTime == DateTime.MinValue ? DateTimeOffset.MinValue : entry.CreationTime; } public ZipStorageFolder(BaseStorageFile backingFile) @@ -95,9 +95,8 @@ public override IAsyncOperation CreateFileAsync(string desiredN } zipFile.IsStreamOwner = true; - var znt = new ZipNameTransform(ContainerPath); - var zipDesiredName = znt.TransformFile(System.IO.Path.Combine(Path, desiredName)); - var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == zipDesiredName); + var zipDesiredName = System.IO.Path.Combine(Path, desiredName); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == zipDesiredName); if (entry != null) { @@ -110,8 +109,7 @@ public override IAsyncOperation CreateFileAsync(string desiredN zipFile.Add(new FileDataSource() { Stream = new MemoryStream() }, zipDesiredName); zipFile.CommitUpdate(); - var wnt = new WindowsNameTransform(ContainerPath); - return new ZipStorageFile(wnt.TransformFile(zipDesiredName), ContainerPath) { BackingFile = BackingFile }; + return new ZipStorageFile(desiredName, ContainerPath) { BackingFile = BackingFile }; */ } }); @@ -135,9 +133,8 @@ public override IAsyncOperation CreateFolderAsync(string desi } zipFile.IsStreamOwner = true; - var znt = new ZipNameTransform(ContainerPath); - var zipDesiredName = znt.TransformDirectory(System.IO.Path.Combine(Path, desiredName)); - var entry = zipFile.GetEntry(zipDesiredName); + var zipDesiredName = System.IO.Path.Combine(Path, desiredName); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == zipDesiredName); zipFile.BeginUpdate(new MemoryArchiveStorage(FileUpdateMode.Direct)); if (entry != null) @@ -152,8 +149,7 @@ public override IAsyncOperation CreateFolderAsync(string desi zipFile.AddDirectory(zipDesiredName); zipFile.CommitUpdate(); - var wnt = new WindowsNameTransform(ContainerPath); - return new ZipStorageFolder(wnt.TransformFile(zipDesiredName), ContainerPath) + return new ZipStorageFolder(desiredName, ContainerPath) { ZipEncoding = ZipEncoding, BackingFile = BackingFile @@ -192,13 +188,11 @@ public override IAsyncOperation GetItemAsync(string name) } zipFile.IsStreamOwner = true; ZipEncoding ??= DetectFileEncoding(zipFile); - var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == name); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == System.IO.Path.Combine(Path, name)); if (entry != null) { - //var wnt = new WindowsNameTransform(ContainerPath); if (entry.IsFolder) { - //return new ZipStorageFolder(wnt.TransformDirectory(DecodeEntryName(entry, ZipEncoding)), ContainerPath, entry) return new ZipStorageFolder(DecodeEntryName(entry, ZipEncoding), ContainerPath, entry) { ZipEncoding = ZipEncoding, @@ -207,7 +201,6 @@ public override IAsyncOperation GetItemAsync(string name) } else { - //return new ZipStorageFile(wnt.TransformFile(DecodeEntryName(entry, ZipEncoding)), ContainerPath, entry) { BackingFile = BackingFile }; return new ZipStorageFile(DecodeEntryName(entry, ZipEncoding), ContainerPath, entry) { BackingFile = BackingFile }; } } @@ -244,12 +237,10 @@ public override IAsyncOperation> GetItemsAsync() } zipFile.IsStreamOwner = true; ZipEncoding ??= DetectFileEncoding(zipFile); - //var wnt = new WindowsNameTransform(ContainerPath, true); // Check with Path.GetFullPath var items = new List(); foreach (var entry in zipFile.Entries) // Returns all items recursively { - string winPath = System.IO.Path.GetFullPath(entry.IsFolder ? DecodeEntryName(entry, ZipEncoding) : DecodeEntryName(entry, ZipEncoding)); - //string winPath = System.IO.Path.GetFullPath(entry.IsDirectory ? wnt.TransformDirectory(DecodeEntryName(entry, ZipEncoding)) : wnt.TransformFile(DecodeEntryName(entry, ZipEncoding))); + string winPath = System.IO.Path.GetFullPath(System.IO.Path.Combine(ContainerPath, DecodeEntryName(entry, ZipEncoding))); if (winPath.StartsWith(Path.WithEnding("\\"), StringComparison.Ordinal)) // Child of self { var split = winPath.Substring(Path.Length).Split('\\', StringSplitOptions.RemoveEmptyEntries); @@ -600,9 +591,7 @@ private async Task GetBasicProperties() return new BaseBasicProperties(); } zipFile.IsStreamOwner = true; - //var znt = new ZipNameTransform(ContainerPath); - //var entry = zipFile.GetEntry(znt.TransformFile(Path)); - var entry = zipFile.Entries.FirstOrDefault(x => x.FileName == Path); + var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == Path); if (entry != null) { return new ZipFolderBasicProperties(entry); @@ -611,13 +600,6 @@ private async Task GetBasicProperties() } } - /*private class FileDataSource : IStaticDataSource - { - public Stream Stream { get; set; } - - public Stream GetSource() => Stream; - }*/ - private class ZipFolderBasicProperties : BaseBasicProperties { private ZipEntry zipEntry; @@ -627,9 +609,9 @@ public ZipFolderBasicProperties(ZipEntry entry) this.zipEntry = entry; } - public override DateTimeOffset DateModified => zipEntry.CreationTime; + public override DateTimeOffset DateModified => zipEntry.CreationTime == DateTime.MinValue ? DateTimeOffset.MinValue : zipEntry.CreationTime; - public override DateTimeOffset ItemDate => zipEntry.CreationTime; + public override DateTimeOffset ItemDate => zipEntry.CreationTime == DateTime.MinValue ? DateTimeOffset.MinValue : zipEntry.CreationTime; public override ulong Size => (ulong)zipEntry.Size; } From a6ff71dc300eac504201a75e1358d6fb4dd2c724 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Mon, 29 Nov 2021 02:42:46 +0100 Subject: [PATCH 09/56] Call uwp LoadPackagedLibrary --- SevenZipWrapper/ArchiveFile.cs | 5 +++-- SevenZipWrapper/Kernel32Dll.cs | 8 ++++---- SevenZipWrapper/SevenZipHandle.cs | 2 +- src/Files.Package/Files.Package.wapproj | 3 +-- .../nupkgs/SharpZipLib.1.3.4.nupkg | Bin 667075 -> 0 bytes 5 files changed, 9 insertions(+), 9 deletions(-) delete mode 100644 src/Files.Package/nupkgs/SharpZipLib.1.3.4.nupkg diff --git a/SevenZipWrapper/ArchiveFile.cs b/SevenZipWrapper/ArchiveFile.cs index c340e37f0ae5..c7bced84bef5 100644 --- a/SevenZipWrapper/ArchiveFile.cs +++ b/SevenZipWrapper/ArchiveFile.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; +using Windows.ApplicationModel; namespace SevenZipExtractor { @@ -254,9 +255,9 @@ private void InitializeAndValidateLibrary() { string currentArchitecture = IntPtr.Size == 4 ? "x86" : "x64"; // magic check - if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SevenZipWrapper", currentArchitecture, "7z.dll"))) + if (File.Exists(Path.Combine(Package.Current.InstalledLocation.Path, "SevenZipWrapper", currentArchitecture, "7z.dll"))) { - this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SevenZipWrapper", currentArchitecture, "7z.dll"); + this.libraryFilePath = Path.Combine("SevenZipWrapper", currentArchitecture, "7z.dll"); } } diff --git a/SevenZipWrapper/Kernel32Dll.cs b/SevenZipWrapper/Kernel32Dll.cs index c69a88a1c7ec..3cce8489160a 100644 --- a/SevenZipWrapper/Kernel32Dll.cs +++ b/SevenZipWrapper/Kernel32Dll.cs @@ -6,14 +6,14 @@ namespace SevenZipExtractor { internal static class Kernel32Dll { - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - internal static extern SafeLibraryHandle LoadLibrary([MarshalAs(UnmanagedType.LPTStr)] string lpFileName); + [DllImport("api-ms-win-core-libraryloader-l2-1-0.dll", SetLastError = true)] + public static extern SafeLibraryHandle LoadPackagedLibrary([MarshalAs(UnmanagedType.LPWStr)] string libraryName, int reserved = 0); - [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll", CharSet = CharSet.Ansi, SetLastError = true)] internal static extern IntPtr GetProcAddress(SafeLibraryHandle hModule, [MarshalAs(UnmanagedType.LPStr)] string procName); [SuppressUnmanagedCodeSecurity] - [DllImport("kernel32.dll")] + [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool FreeLibrary(IntPtr hModule); } diff --git a/SevenZipWrapper/SevenZipHandle.cs b/SevenZipWrapper/SevenZipHandle.cs index 8fa45fbcfb7a..55ad8f62a3bc 100644 --- a/SevenZipWrapper/SevenZipHandle.cs +++ b/SevenZipWrapper/SevenZipHandle.cs @@ -10,7 +10,7 @@ internal class SevenZipHandle : IDisposable public SevenZipHandle(string sevenZipLibPath) { - this.sevenZipSafeHandle = Kernel32Dll.LoadLibrary(sevenZipLibPath); + this.sevenZipSafeHandle = Kernel32Dll.LoadPackagedLibrary(sevenZipLibPath); if (this.sevenZipSafeHandle.IsInvalid) { diff --git a/src/Files.Package/Files.Package.wapproj b/src/Files.Package/Files.Package.wapproj index 03cf8b7edf23..b196d98898fd 100644 --- a/src/Files.Package/Files.Package.wapproj +++ b/src/Files.Package/Files.Package.wapproj @@ -479,7 +479,6 @@ - - + \ No newline at end of file diff --git a/src/Files.Package/nupkgs/SharpZipLib.1.3.4.nupkg b/src/Files.Package/nupkgs/SharpZipLib.1.3.4.nupkg deleted file mode 100644 index ef5a82d42d59d8fd0a9b31ac58c42628ba175ff3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 667075 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Νiw};ks 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ⅆ41K1Yg5 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ƋiAN*f1 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 CAJGi} From 9976146892fb1578316a4eeadb220ec0fada518c Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Mon, 29 Nov 2021 03:09:45 +0100 Subject: [PATCH 10/56] Open 7z, zip, rar --- .../Win32StorageEnumerator.cs | 2 +- .../Filesystem/StorageItems/ZipStorageFile.cs | 9 ++++++-- .../StorageItems/ZipStorageFolder.cs | 23 +++++++++++++++---- .../Previews/ArchivePreviewViewModel.cs | 2 +- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/Files/Filesystem/StorageEnumerators/Win32StorageEnumerator.cs b/src/Files/Filesystem/StorageEnumerators/Win32StorageEnumerator.cs index 9572df9e52c2..c3764e54d479 100644 --- a/src/Files/Filesystem/StorageEnumerators/Win32StorageEnumerator.cs +++ b/src/Files/Filesystem/StorageEnumerators/Win32StorageEnumerator.cs @@ -308,7 +308,7 @@ CancellationToken cancellationToken } else { - if (".zip".Equals(itemFileExtension, StringComparison.OrdinalIgnoreCase) && await ZipStorageFolder.CheckDefaultZipApp(itemPath)) + if (ZipStorageFolder.Extensions.Any(x => x.Equals(itemFileExtension, StringComparison.OrdinalIgnoreCase)) && await ZipStorageFolder.CheckDefaultZipApp(itemPath)) { return new ZipItem(null, dateReturnFormat) { diff --git a/src/Files/Filesystem/StorageItems/ZipStorageFile.cs b/src/Files/Filesystem/StorageItems/ZipStorageFile.cs index 9afb87ec92d1..82eeaf53e172 100644 --- a/src/Files/Filesystem/StorageItems/ZipStorageFile.cs +++ b/src/Files/Filesystem/StorageItems/ZipStorageFile.cs @@ -358,10 +358,15 @@ public static IAsyncOperation FromPathAsync(string path) { return AsyncInfo.Run(cancellationToken => { - var marker = path.IndexOf(".zip", StringComparison.OrdinalIgnoreCase); + var ext = ZipStorageFolder.Extensions.FirstOrDefault(x => path.Contains(x, StringComparison.OrdinalIgnoreCase)); + if (string.IsNullOrEmpty(ext)) + { + return Task.FromResult(null); + } + var marker = path.IndexOf(ext, StringComparison.OrdinalIgnoreCase); if (marker != -1) { - var containerPath = path.Substring(0, marker + ".zip".Length); + var containerPath = path.Substring(0, marker + ext.Length); if (path == containerPath) { return Task.FromResult(null); // Root diff --git a/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs b/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs index 4b83edba07d8..7a8f81fea57c 100644 --- a/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs +++ b/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs @@ -21,6 +21,11 @@ public sealed class ZipStorageFolder : BaseStorageFolder { public Encoding ZipEncoding { get; set; } = null; + public static List Extensions => new List() + { + ".zip", ".7z", ".rar" + }; + private static bool? IsDefaultZipApp; public static async Task CheckDefaultZipApp(string filePath) { @@ -463,10 +468,15 @@ public static IAsyncOperation FromPathAsync(string path) { return AsyncInfo.Run(async (cancellationToken) => { - var marker = path.IndexOf(".zip", StringComparison.OrdinalIgnoreCase); + var ext = ZipStorageFolder.Extensions.FirstOrDefault(x => path.Contains(x, StringComparison.OrdinalIgnoreCase)); + if (string.IsNullOrEmpty(ext)) + { + return null; + } + var marker = path.IndexOf(ext, StringComparison.OrdinalIgnoreCase); if (marker != -1) { - var containerPath = path.Substring(0, marker + ".zip".Length); + var containerPath = path.Substring(0, marker + ext.Length); if (!await CheckDefaultZipApp(path)) { return null; @@ -494,10 +504,15 @@ public static IAsyncOperation FromStorageFileAsync(BaseStorag public static bool IsZipPath(string path) { - var marker = path.IndexOf(".zip", StringComparison.OrdinalIgnoreCase); + var ext = ZipStorageFolder.Extensions.FirstOrDefault(x => path.Contains(x, StringComparison.OrdinalIgnoreCase)); + if (string.IsNullOrEmpty(ext)) + { + return false; + } + var marker = path.IndexOf(ext, StringComparison.OrdinalIgnoreCase); if (marker != -1) { - marker += ".zip".Length; + marker += ext.Length; if (marker == path.Length || path[marker] == '\\') { return true; diff --git a/src/Files/ViewModels/Previews/ArchivePreviewViewModel.cs b/src/Files/ViewModels/Previews/ArchivePreviewViewModel.cs index b4f8dc3cedf7..6d9c1aea2e3b 100644 --- a/src/Files/ViewModels/Previews/ArchivePreviewViewModel.cs +++ b/src/Files/ViewModels/Previews/ArchivePreviewViewModel.cs @@ -14,7 +14,7 @@ public class ArchivePreviewViewModel : BasePreviewModel { public static List Extensions => new List() { - ".zip", + ".zip", ".7z", ".rar" }; public ArchivePreviewViewModel(ListedItem item) : base(item) From 85f6aafbbce1f34b98b9dba1bcf1443247b4de6b Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Mon, 29 Nov 2021 03:32:48 +0100 Subject: [PATCH 11/56] Properly dispose streams & detect failed ops --- .../MessageHandlers/FileOperationsHandler.cs | 85 ++++++++++++++++--- .../Filesystem/StorageItems/ZipStorageFile.cs | 15 +++- 2 files changed, 83 insertions(+), 17 deletions(-) diff --git a/src/Files.Launcher/MessageHandlers/FileOperationsHandler.cs b/src/Files.Launcher/MessageHandlers/FileOperationsHandler.cs index f74637736a65..a833dbbe40bc 100644 --- a/src/Files.Launcher/MessageHandlers/FileOperationsHandler.cs +++ b/src/Files.Launcher/MessageHandlers/FileOperationsHandler.cs @@ -155,9 +155,20 @@ await Win32API.StartSTATask(() => var shellOperationResult = new ShellOperationResult(); - using var shd = new ShellFolder(Path.GetDirectoryName(filePath)); - op.QueueNewItemOperation(shd, Path.GetFileName(filePath), - (string)message["fileop"] == "CreateFolder" ? FileAttributes.Directory : FileAttributes.Normal, template); + if (!Extensions.IgnoreExceptions(() => + { + using var shd = new ShellFolder(Path.GetDirectoryName(filePath)); + op.QueueNewItemOperation(shd, Path.GetFileName(filePath), + (string)message["fileop"] == "CreateFolder" ? FileAttributes.Directory : FileAttributes.Normal, template); + })) + { + shellOperationResult.Items.Add(new ShellOperationItemResult() + { + Succeeded = false, + Destination = filePath, + HRresult = (int)-1 + }); + } var createTcs = new TaskCompletionSource(); op.PostNewItem += (s, e) => @@ -227,8 +238,19 @@ await Win32API.StartSTATask(() => for (var i = 0; i < fileToDeletePath.Length; i++) { - using var shi = new ShellItem(fileToDeletePath[i]); - op.QueueDeleteOperation(shi); + if (!Extensions.IgnoreExceptions(() => + { + using var shi = new ShellItem(fileToDeletePath[i]); + op.QueueDeleteOperation(shi); + })) + { + shellOperationResult.Items.Add(new ShellOperationItemResult() + { + Succeeded = false, + Source = fileToDeletePath[i], + HRresult = (int)-1 + }); + } } progressHandler.OwnerWindow = op.OwnerWindow; @@ -300,8 +322,19 @@ await Win32API.StartSTATask(() => | ShellFileOperations.OperationFlags.NoErrorUI; op.Options |= !overwriteOnRename ? ShellFileOperations.OperationFlags.RenameOnCollision : 0; - using var shi = new ShellItem(fileToRenamePath); - op.QueueRenameOperation(shi, newName); + if (!Extensions.IgnoreExceptions(() => + { + using var shi = new ShellItem(fileToRenamePath); + op.QueueRenameOperation(shi, newName); + })) + { + shellOperationResult.Items.Add(new ShellOperationItemResult() + { + Succeeded = false, + Source = fileToRenamePath, + HRresult = (int)-1 + }); + } progressHandler.OwnerWindow = op.OwnerWindow; progressHandler.AddOperation(operationID); @@ -363,10 +396,22 @@ await Win32API.StartSTATask(() => for (var i = 0; i < fileToMovePath.Length; i++) { - using (ShellItem shi = new ShellItem(fileToMovePath[i])) - using (ShellFolder shd = new ShellFolder(Path.GetDirectoryName(moveDestination[i]))) + if (!Extensions.IgnoreExceptions(() => + { + using (ShellItem shi = new ShellItem(fileToMovePath[i])) + using (ShellFolder shd = new ShellFolder(Path.GetDirectoryName(moveDestination[i]))) + { + op.QueueMoveOperation(shi, shd, Path.GetFileName(moveDestination[i])); + } + })) { - op.QueueMoveOperation(shi, shd, Path.GetFileName(moveDestination[i])); + shellOperationResult.Items.Add(new ShellOperationItemResult() + { + Succeeded = false, + Source = fileToMovePath[i], + Destination = moveDestination[i], + HRresult = (int)-1 + }); } } @@ -438,10 +483,22 @@ await Win32API.StartSTATask(() => for (var i = 0; i < fileToCopyPath.Length; i++) { - using (ShellItem shi = new ShellItem(fileToCopyPath[i])) - using (ShellFolder shd = new ShellFolder(Path.GetDirectoryName(copyDestination[i]))) + if (!Extensions.IgnoreExceptions(() => + { + using (ShellItem shi = new ShellItem(fileToCopyPath[i])) + using (ShellFolder shd = new ShellFolder(Path.GetDirectoryName(copyDestination[i]))) + { + op.QueueCopyOperation(shi, shd, Path.GetFileName(copyDestination[i])); + } + })) { - op.QueueCopyOperation(shi, shd, Path.GetFileName(copyDestination[i])); + shellOperationResult.Items.Add(new ShellOperationItemResult() + { + Succeeded = false, + Source = fileToCopyPath[i], + Destination = copyDestination[i], + HRresult = (int)-1 + }); } } @@ -676,7 +733,7 @@ await Win32API.StartSTATask(() => bool success = false; if (string.IsNullOrEmpty(compatOptions) || compatOptions == "~") { - success = Win32API.RunPowershellCommand(@$"Remove-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers' -Name '{filePath}' | Out-Null", false); + success = Win32API.RunPowershellCommand(@$"Remove-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers' -Name '{filePath}' | Out-Null", false); } else { diff --git a/src/Files/Filesystem/StorageItems/ZipStorageFile.cs b/src/Files/Filesystem/StorageItems/ZipStorageFile.cs index 82eeaf53e172..71543452ded0 100644 --- a/src/Files/Filesystem/StorageItems/ZipStorageFile.cs +++ b/src/Files/Filesystem/StorageItems/ZipStorageFile.cs @@ -145,7 +145,10 @@ public override IAsyncOperation CopyAsync(IStorageFolder destin { var destFolder = destinationFolder.AsBaseStorageFolder(); var destFile = await destFolder.CreateFileAsync(desiredNewName, option.Convert()); - entry.Extract(await destFile.OpenStreamForWriteAsync()); + using (var outStream = await destFile.OpenStreamForWriteAsync()) + { + entry.Extract(outStream); + } return destFile; } return null; @@ -168,7 +171,10 @@ public override IAsyncAction CopyAndReplaceAsync(IStorageFile fileToReplace) if (entry != null) { using var hDestFile = fileToReplace.CreateSafeFileHandle(FileAccess.ReadWrite); - entry.Extract(new FileStream(hDestFile, FileAccess.Write)); + using (var outStream = new FileStream(hDestFile, FileAccess.Write)) + { + entry.Extract(outStream); + } } } }); @@ -443,7 +449,10 @@ private StreamedFileDataRequestedHandler ZipDataStreamingHandler(string name) var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == name); if (entry != null) { - entry.Extract(request.AsStreamForWrite()); + using (var outStream = request.AsStreamForWrite()) + { + entry.Extract(outStream); + } request.Dispose(); } else From 15b07ab52b59ef992af8b503752dd4abfc6a0a36 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Mon, 29 Nov 2021 21:16:33 +0100 Subject: [PATCH 12/56] Restore extract commands --- src/Files/Helpers/ZipHelpers.cs | 10 ++-------- .../Interacts/BaseLayoutCommandImplementationModel.cs | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Files/Helpers/ZipHelpers.cs b/src/Files/Helpers/ZipHelpers.cs index 64289e28b4c9..3aadafe64697 100644 --- a/src/Files/Helpers/ZipHelpers.cs +++ b/src/Files/Helpers/ZipHelpers.cs @@ -40,7 +40,6 @@ public static async Task ExtractArchive(BaseStorageFile archive, BaseStorageFold return; } - //var wnt = new WindowsNameTransform(destinationFolder.Path); var zipEncoding = ZipStorageFolder.DetectFileEncoding(zipFile); var directories = new List(); @@ -62,7 +61,7 @@ public static async Task ExtractArchive(BaseStorageFile archive, BaseStorageFold if (!NativeFileOperationsHelper.CreateDirectoryFromApp(dir, IntPtr.Zero)) { var dirName = destinationFolder.Path; - foreach (var component in dir.Substring(destinationFolder.Path.Length).Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries)) + foreach (var component in dir.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries)) { dirName = Path.Combine(dirName, component); NativeFileOperationsHelper.CreateDirectoryFromApp(dirName, IntPtr.Zero); @@ -92,13 +91,8 @@ public static async Task ExtractArchive(BaseStorageFile archive, BaseStorageFold { return; } - if (entry.IsEncrypted) - { - App.Logger.Info($"Skipped encrypted zip entry: {entry.FileName}"); - continue; // TODO: support password protected archives - } - string filePath = ZipStorageFolder.DecodeEntryName(entry, zipEncoding); + string filePath = Path.Combine(destinationFolder.Path, ZipStorageFolder.DecodeEntryName(entry, zipEncoding)); var hFile = NativeFileOperationsHelper.CreateFileForWrite(filePath); if (hFile.IsInvalid) diff --git a/src/Files/Interacts/BaseLayoutCommandImplementationModel.cs b/src/Files/Interacts/BaseLayoutCommandImplementationModel.cs index 794fa6dbc249..d18ecf851895 100644 --- a/src/Files/Interacts/BaseLayoutCommandImplementationModel.cs +++ b/src/Files/Interacts/BaseLayoutCommandImplementationModel.cs @@ -752,7 +752,7 @@ public async void DecompressArchiveToChildFolder() if (currentFolder != null) { - destinationFolder = await FilesystemTasks.Wrap(() => currentFolder.CreateFolderAsync(Path.GetFileNameWithoutExtension(archive.Path), CreationCollisionOption.OpenIfExists).AsTask()); + destinationFolder = await FilesystemTasks.Wrap(() => currentFolder.CreateFolderAsync(Path.GetFileNameWithoutExtension(archive.Path), CreationCollisionOption.GenerateUniqueName).AsTask()); } if (archive != null && destinationFolder != null) From 230f3f0d23ec2b61ae08d62d145707045f9582d9 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 4 Dec 2021 14:38:26 +0100 Subject: [PATCH 13/56] Removed unnecessary enconding detection --- src/Files/Files.csproj | 3 --- .../StorageItems/ZipStorageFolder.cs | 25 +++---------------- src/Files/Helpers/ZipHelpers.cs | 8 +++--- 3 files changed, 6 insertions(+), 30 deletions(-) diff --git a/src/Files/Files.csproj b/src/Files/Files.csproj index 3ac9d73f2304..eadc90b0b655 100644 --- a/src/Files/Files.csproj +++ b/src/Files/Files.csproj @@ -1477,9 +1477,6 @@ 2.0.7 - - 1.2.0 - diff --git a/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs b/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs index 7a8f81fea57c..78653d27da70 100644 --- a/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs +++ b/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs @@ -19,8 +19,6 @@ namespace Files.Filesystem.StorageItems { public sealed class ZipStorageFolder : BaseStorageFolder { - public Encoding ZipEncoding { get; set; } = null; - public static List Extensions => new List() { ".zip", ".7z", ".rar" @@ -42,18 +40,6 @@ public static async Task CheckDefaultZipApp(string filePath) return IsDefaultZipApp ?? await queryFileAssoc(); } - public static string DecodeEntryName(ZipEntry entry, Encoding zipEncoding) - { - // TODO - return entry.FileName; - } - - public static Encoding DetectFileEncoding(ArchiveFile zipFile) - { - // TODO - return Encoding.UTF8; - } - public ZipStorageFolder(string path, string containerPath) { Name = System.IO.Path.GetFileName(path.TrimEnd('\\', '/')); @@ -156,7 +142,6 @@ public override IAsyncOperation CreateFolderAsync(string desi return new ZipStorageFolder(desiredName, ContainerPath) { - ZipEncoding = ZipEncoding, BackingFile = BackingFile }; */ @@ -192,21 +177,19 @@ public override IAsyncOperation GetItemAsync(string name) return null; } zipFile.IsStreamOwner = true; - ZipEncoding ??= DetectFileEncoding(zipFile); var entry = zipFile.Entries.FirstOrDefault(x => System.IO.Path.Combine(ContainerPath, x.FileName) == System.IO.Path.Combine(Path, name)); if (entry != null) { if (entry.IsFolder) { - return new ZipStorageFolder(DecodeEntryName(entry, ZipEncoding), ContainerPath, entry) + return new ZipStorageFolder(entry.FileName, ContainerPath, entry) { - ZipEncoding = ZipEncoding, BackingFile = BackingFile }; } else { - return new ZipStorageFile(DecodeEntryName(entry, ZipEncoding), ContainerPath, entry) { BackingFile = BackingFile }; + return new ZipStorageFile(entry.FileName, ContainerPath, entry) { BackingFile = BackingFile }; } } return null; @@ -241,11 +224,10 @@ public override IAsyncOperation> GetItemsAsync() return null; } zipFile.IsStreamOwner = true; - ZipEncoding ??= DetectFileEncoding(zipFile); var items = new List(); foreach (var entry in zipFile.Entries) // Returns all items recursively { - string winPath = System.IO.Path.GetFullPath(System.IO.Path.Combine(ContainerPath, DecodeEntryName(entry, ZipEncoding))); + string winPath = System.IO.Path.GetFullPath(System.IO.Path.Combine(ContainerPath, entry.FileName)); if (winPath.StartsWith(Path.WithEnding("\\"), StringComparison.Ordinal)) // Child of self { var split = winPath.Substring(Path.Length).Split('\\', StringSplitOptions.RemoveEmptyEntries); @@ -258,7 +240,6 @@ public override IAsyncOperation> GetItemsAsync() { items.Add(new ZipStorageFolder(itemPath, ContainerPath, entry) { - ZipEncoding = ZipEncoding, BackingFile = BackingFile }); } diff --git a/src/Files/Helpers/ZipHelpers.cs b/src/Files/Helpers/ZipHelpers.cs index 3aadafe64697..3e9d225a42d2 100644 --- a/src/Files/Helpers/ZipHelpers.cs +++ b/src/Files/Helpers/ZipHelpers.cs @@ -40,13 +40,11 @@ public static async Task ExtractArchive(BaseStorageFile archive, BaseStorageFold return; } - var zipEncoding = ZipStorageFolder.DetectFileEncoding(zipFile); - var directories = new List(); try { - directories.AddRange(directoryEntries.Select((entry) => ZipStorageFolder.DecodeEntryName(entry, zipEncoding))); - directories.AddRange(fileEntries.Select((entry) => Path.GetDirectoryName(ZipStorageFolder.DecodeEntryName(entry, zipEncoding)))); + directories.AddRange(directoryEntries.Select((entry) => entry.FileName)); + directories.AddRange(fileEntries.Select((entry) => Path.GetDirectoryName(entry.FileName))); } catch (Exception ex) { @@ -92,7 +90,7 @@ public static async Task ExtractArchive(BaseStorageFile archive, BaseStorageFold return; } - string filePath = Path.Combine(destinationFolder.Path, ZipStorageFolder.DecodeEntryName(entry, zipEncoding)); + string filePath = Path.Combine(destinationFolder.Path, entry.FileName); var hFile = NativeFileOperationsHelper.CreateFileForWrite(filePath); if (hFile.IsInvalid) From 485c546ad89fd49be4b5ee42e08ee30467b80d30 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 4 Dec 2021 15:46:14 +0100 Subject: [PATCH 14/56] [WIP] Add preference to open archives in Files --- src/Files/Filesystem/StorageItems/ZipStorageFolder.cs | 8 +++++--- src/Files/Services/IPreferencesSettingsService.cs | 5 +++++ .../Services/Implementation/PreferencesSettingsService.cs | 7 +++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs b/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs index 78653d27da70..9083e64e7039 100644 --- a/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs +++ b/src/Files/Filesystem/StorageItems/ZipStorageFolder.cs @@ -1,5 +1,7 @@ using Files.Extensions; using Files.Helpers; +using Files.Services; +using Microsoft.Toolkit.Mvvm.DependencyInjection; using Microsoft.Toolkit.Uwp; using SevenZipExtractor; using System; @@ -24,20 +26,20 @@ public sealed class ZipStorageFolder : BaseStorageFolder ".zip", ".7z", ".rar" }; - private static bool? IsDefaultZipApp; public static async Task CheckDefaultZipApp(string filePath) { + IUserSettingsService userSettingsService = Ioc.Default.GetService(); Func> queryFileAssoc = async () => { var assoc = await NativeWinApiHelper.GetFileAssociationAsync(filePath); if (assoc != null) { - IsDefaultZipApp = assoc == Package.Current.Id.FamilyName + return assoc == Package.Current.Id.FamilyName || assoc.Equals(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe"), StringComparison.OrdinalIgnoreCase); } return true; }; - return IsDefaultZipApp ?? await queryFileAssoc(); + return userSettingsService.PreferencesSettingsService.OpenArchivesInFiles || await queryFileAssoc(); } public ZipStorageFolder(string path, string containerPath) diff --git a/src/Files/Services/IPreferencesSettingsService.cs b/src/Files/Services/IPreferencesSettingsService.cs index f421ce9c80d3..7f050f0e1877 100644 --- a/src/Files/Services/IPreferencesSettingsService.cs +++ b/src/Files/Services/IPreferencesSettingsService.cs @@ -104,5 +104,10 @@ public interface IPreferencesSettingsService : INotifyPropertyChanged /// A list containing all paths to tabs closed on last session. /// List LastSessionTabList { get; set; } + + ///

+ /// Gets or sets a value indicating whether or not to open archives in Files. + /// + bool OpenArchivesInFiles { get; set; } } } diff --git a/src/Files/Services/Implementation/PreferencesSettingsService.cs b/src/Files/Services/Implementation/PreferencesSettingsService.cs index 62e7643ef6cc..b56cf56593cb 100644 --- a/src/Files/Services/Implementation/PreferencesSettingsService.cs +++ b/src/Files/Services/Implementation/PreferencesSettingsService.cs @@ -32,6 +32,7 @@ public override void RaiseOnSettingChangedEvent(object sender, EventArguments.Se case nameof(ContinueLastSessionOnStartUp): case nameof(OpenNewTabOnStartup): case nameof(AlwaysOpenNewInstance): + case nameof(OpenArchivesInFiles): Microsoft.AppCenter.Analytics.Analytics.TrackEvent($"{e.settingName} {e.newValue}"); break; } @@ -158,5 +159,11 @@ public List LastSessionTabList get => Get>(null); set => Set(value); } + + public bool OpenArchivesInFiles + { + get => Get(true); + set => Set(value); + } } } From 02849e2b201dd5f78fb2dfc8531069290fb9ebf9 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 4 Dec 2021 16:14:34 +0100 Subject: [PATCH 15/56] Show icons in 7z files --- src/Files.Launcher/Win32API.cs | 39 ++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/Files.Launcher/Win32API.cs b/src/Files.Launcher/Win32API.cs index e3edf15bc383..826c5bed7530 100644 --- a/src/Files.Launcher/Win32API.cs +++ b/src/Files.Launcher/Win32API.cs @@ -165,8 +165,8 @@ public static (string icon, string overlay) GetFileIconAndOverlay(string path, i if (!onlyGetOverlay) { - using var shellItem = new Vanara.Windows.Shell.ShellItem(path); - if (shellItem.IShellItem is Shell32.IShellItemImageFactory fctry) + using var shellItem = Extensions.IgnoreExceptions(() => new Vanara.Windows.Shell.ShellItem(path), Program.Logger); + if (shellItem != null && shellItem.IShellItem is Shell32.IShellItemImageFactory fctry) { var flags = Shell32.SIIGBF.SIIGBF_BIGGERSIZEOK; if (thumbnailSize < 80) flags |= Shell32.SIIGBF.SIIGBF_ICONONLY; @@ -184,15 +184,16 @@ public static (string icon, string overlay) GetFileIconAndOverlay(string path, i } } - if (getOverlay) + if (getOverlay || (!onlyGetOverlay && iconStr == null)) { var shfi = new Shell32.SHFILEINFO(); - var ret = Shell32.SHGetFileInfo( - path, - 0, - ref shfi, - Shell32.SHFILEINFO.Size, - Shell32.SHGFI.SHGFI_OVERLAYINDEX | Shell32.SHGFI.SHGFI_ICON | Shell32.SHGFI.SHGFI_SYSICONINDEX | Shell32.SHGFI.SHGFI_ICONLOCATION); + var uFlags = Shell32.SHGFI.SHGFI_OVERLAYINDEX | Shell32.SHGFI.SHGFI_ICON; + if (!onlyGetOverlay && iconStr == null) + { + uFlags |= Shell32.SHGFI.SHGFI_USEFILEATTRIBUTES | Shell32.SHGFI.SHGFI_SYSICONINDEX | Shell32.SHGFI.SHGFI_ICONLOCATION; + } + // TODO: pass FileAttributes.Directory for folders (add "isFolder" parameter) + var ret = Shell32.SHGetFileInfo(path, FileAttributes.Normal, ref shfi, Shell32.SHFILEINFO.Size, uFlags); if (ret == IntPtr.Zero) { return (iconStr, null); @@ -202,13 +203,29 @@ public static (string icon, string overlay) GetFileIconAndOverlay(string path, i lock (lockObject) { - if (!Shell32.SHGetImageList(Shell32.SHIL.SHIL_LARGE, typeof(ComCtl32.IImageList).GUID, out var imageList).Succeeded) + if (!Shell32.SHGetImageList(Shell32.SHIL.SHIL_EXTRALARGE, typeof(ComCtl32.IImageList).GUID, out var imageList).Succeeded) { return (iconStr, null); } + var iconIdx = shfi.iIcon & 0xFFFFFF; + if (iconIdx != 0 && !onlyGetOverlay && iconStr == null) + { + // Could not fetch thumbnail, load simple icon + using var hIcon = imageList.GetIcon(iconIdx, ComCtl32.IMAGELISTDRAWFLAGS.ILD_TRANSPARENT); + if (!hIcon.IsNull && !hIcon.IsInvalid) + { + using (var icon = hIcon.ToIcon()) + using (var image = icon.ToBitmap()) + { + byte[] bitmapData = (byte[])new ImageConverter().ConvertTo(image, typeof(byte[])); + iconStr = Convert.ToBase64String(bitmapData, 0, bitmapData.Length); + } + } + } + var overlayIdx = shfi.iIcon >> 24; - if (overlayIdx != 0) + if (overlayIdx != 0 && getOverlay) { var overlayImage = imageList.GetOverlayImage(overlayIdx); using var hOverlay = imageList.GetIcon(overlayImage, ComCtl32.IMAGELISTDRAWFLAGS.ILD_TRANSPARENT); From 5b0a8b70197ed7f74fcc2fb9867898e453916757 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sat, 4 Dec 2021 16:33:20 +0100 Subject: [PATCH 16/56] Load correct size icon and overlay (#7052) --- src/Files.Launcher/Files.Launcher.csproj | 2 +- src/Files.Launcher/Win32API.cs | 9 ++++++++- src/Files/Helpers/FileThumbnailHelper.cs | 4 ++-- src/Files/ViewModels/ItemViewModel.cs | 4 ++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Files.Launcher/Files.Launcher.csproj b/src/Files.Launcher/Files.Launcher.csproj index f8a180f0c81e..435fc6ff0074 100644 --- a/src/Files.Launcher/Files.Launcher.csproj +++ b/src/Files.Launcher/Files.Launcher.csproj @@ -7,7 +7,7 @@ x86;x64;arm64 win-arm64;win-x86;win-x64 false - 8.0 + 9.0 MinimumRecommendedRules.ruleset diff --git a/src/Files.Launcher/Win32API.cs b/src/Files.Launcher/Win32API.cs index 826c5bed7530..c17db56b38b5 100644 --- a/src/Files.Launcher/Win32API.cs +++ b/src/Files.Launcher/Win32API.cs @@ -203,7 +203,14 @@ public static (string icon, string overlay) GetFileIconAndOverlay(string path, i lock (lockObject) { - if (!Shell32.SHGetImageList(Shell32.SHIL.SHIL_EXTRALARGE, typeof(ComCtl32.IImageList).GUID, out var imageList).Succeeded) + var imageListSize = thumbnailSize switch + { + <= 16 => Shell32.SHIL.SHIL_SMALL, + <= 32 => Shell32.SHIL.SHIL_LARGE, + <= 48 => Shell32.SHIL.SHIL_EXTRALARGE, + _ => Shell32.SHIL.SHIL_JUMBO, + }; + if (!Shell32.SHGetImageList(imageListSize, typeof(ComCtl32.IImageList).GUID, out var imageList).Succeeded) { return (iconStr, null); } diff --git a/src/Files/Helpers/FileThumbnailHelper.cs b/src/Files/Helpers/FileThumbnailHelper.cs index 38cc87efb5f8..e2f9514db5d5 100644 --- a/src/Files/Helpers/FileThumbnailHelper.cs +++ b/src/Files/Helpers/FileThumbnailHelper.cs @@ -39,7 +39,7 @@ public static class FileThumbnailHelper return (null, null); } - public static async Task LoadOverlayAsync(string filePath) + public static async Task LoadOverlayAsync(string filePath, uint thumbnailSize) { var connection = await AppServiceConnectionHelper.Instance; if (connection != null) @@ -48,7 +48,7 @@ public static async Task LoadOverlayAsync(string filePath) { { "Arguments", "GetIconOverlay" }, { "filePath", filePath }, - { "thumbnailSize", 0 }, // Must pass in arbitrary int value for this to work + { "thumbnailSize", thumbnailSize }, { "isOverlayOnly", true } }; var (status, response) = await connection.SendMessageForResponseAsync(value); diff --git a/src/Files/ViewModels/ItemViewModel.cs b/src/Files/ViewModels/ItemViewModel.cs index 4939dbed280f..06859bde8639 100644 --- a/src/Files/ViewModels/ItemViewModel.cs +++ b/src/Files/ViewModels/ItemViewModel.cs @@ -824,7 +824,7 @@ await CoreApplication.MainView.DispatcherQueue.EnqueueAsync(async () => wasIconLoaded = true; } - var overlayInfo = await FileThumbnailHelper.LoadOverlayAsync(item.ItemPath); + var overlayInfo = await FileThumbnailHelper.LoadOverlayAsync(item.ItemPath, thumbnailSize); if (overlayInfo != null) { await CoreApplication.MainView.DispatcherQueue.EnqueueAsync(async () => @@ -883,7 +883,7 @@ await CoreApplication.MainView.DispatcherQueue.EnqueueAsync(async () => wasIconLoaded = true; } - var overlayInfo = await FileThumbnailHelper.LoadOverlayAsync(item.ItemPath); + var overlayInfo = await FileThumbnailHelper.LoadOverlayAsync(item.ItemPath, thumbnailSize); if (overlayInfo != null) { await CoreApplication.MainView.DispatcherQueue.EnqueueAsync(async () => From 6de4468cb4be5f07af95aaa2169d5b77e069b796 Mon Sep 17 00:00:00 2001 From: Marco Gavelli Date: Sun, 5 Dec 2021 16:18:48 +0100 Subject: [PATCH 17/56] [TEST] Switch to SevenZipSharp --- Files.sln | 212 +- SevenZip/7z.dll | Bin 0 -> 1149952 bytes SevenZip/7z64.dll | Bin 0 -> 1691136 bytes SevenZip/ArchiveEmulationStreamProxy.cs | 83 + SevenZip/ArchiveExtractCallback.cs | 548 +++++ SevenZip/ArchiveFileInfo.cs | 139 ++ SevenZip/ArchiveOpenCallback.cs | 196 ++ SevenZip/ArchiveProperty.cs | 82 + SevenZip/ArchiveUpdateCallback.cs | 809 +++++++ SevenZip/COM.cs | 1127 ++++++++++ SevenZip/CallbackBase.cs | 113 + SevenZip/CompressionMode.cs | 21 + .../EventArguments/ExtractFileCallback.cs | 11 + .../EventArguments/ExtractFileCallbackArgs.cs | 110 + .../ExtractFileCallbackReason.cs | 27 + SevenZip/EventArguments/FileInfoEventArgs.cs | 36 + SevenZip/EventArguments/FileNameEventArgs.cs | 46 + .../EventArguments/FileOverwriteEventArgs.cs | 33 + SevenZip/EventArguments/ICancellable.cs | 18 + SevenZip/EventArguments/IntEventArgs.cs | 30 + SevenZip/EventArguments/OpenEventArgs.cs | 32 + .../EventArguments/PercentDoneEventArgs.cs | 43 + SevenZip/EventArguments/ProgressEventArgs.cs | 26 + SevenZip/EventSynchronizationStrategy.cs | 25 + .../Exceptions/CompressionFailedException.cs | 49 + .../Exceptions/ExtractionFailedException.cs | 48 + SevenZip/Exceptions/LzmaException.cs | 44 + .../Exceptions/SevenZipArchiveException.cs | 51 + .../SevenZipCompressionFailedException.cs | 49 + SevenZip/Exceptions/SevenZipException.cs | 66 + .../SevenZipExtractionFailedException.cs | 48 + .../SevenZipInvalidFileNamesException.cs | 48 + .../Exceptions/SevenZipLibraryException.cs | 48 + .../SevenZipSfxValidationException.cs | 44 + SevenZip/FileChecker.cs | 254 +++ SevenZip/Formats.cs | 594 ++++++ SevenZip/InternalCompressionMode.cs | 22 + SevenZip/LZMA/LzmaDecodeStream.cs | 197 ++ SevenZip/LZMA/LzmaEncodeStream.cs | 274 +++ SevenZip/LZMA/LzmaProgressCallback.cs | 57 + SevenZip/LibraryFeature.cs | 97 + SevenZip/LibraryManager.cs | 509 +++++ SevenZip/NativeMethods.cs | 47 + SevenZip/Properties/AssemblyInfo.cs | 20 + SevenZip/SevenZip.csproj | 72 + SevenZip/SevenZip.csproj.DotSettings | 3 + SevenZip/SevenZip.snk | Bin 0 -> 596 bytes SevenZip/SevenZipBase.cs | 321 +++ SevenZip/SevenZipCompressor.cs | 1866 +++++++++++++++++ SevenZip/SevenZipCompressorAsynchronous.cs | 466 ++++ SevenZip/SevenZipExtractor.cs | 1468 +++++++++++++ SevenZip/SevenZipExtractorAsynchronous.cs | 280 +++ SevenZip/SevenZipSfx.cs | 505 +++++ SevenZip/StreamWrappers.cs | 496 +++++ SevenZip/UpdateData.cs | 21 + SevenZip/ZipEncryptionMethod.cs | 29 + SevenZip/gpl.txt | 674 ++++++ SevenZip/lgpl.txt | 165 ++ SevenZip/sdk/Common/CRC.cs | 55 + SevenZip/sdk/Common/InBuffer.cs | 103 + SevenZip/sdk/Common/OutBuffer.cs | 69 + SevenZip/sdk/Compress/LZ/IMatchFinder.cs | 24 + SevenZip/sdk/Compress/LZ/LzBinTree.cs | 389 ++++ SevenZip/sdk/Compress/LZ/LzInWindow.cs | 182 ++ SevenZip/sdk/Compress/LZ/LzOutWindow.cs | 109 + SevenZip/sdk/Compress/LZMA/LzmaBase.cs | 92 + SevenZip/sdk/Compress/LZMA/LzmaDecoder.cs | 465 ++++ SevenZip/sdk/Compress/LZMA/LzmaEncoder.cs | 1572 ++++++++++++++ .../sdk/Compress/RangeCoder/RangeCoder.cs | 233 ++ .../sdk/Compress/RangeCoder/RangeCoderBit.cs | 130 ++ .../Compress/RangeCoder/RangeCoderBitTree.cs | 157 ++ SevenZip/sdk/ICoder.cs | 176 ++ SevenZipWrapper/ArchiveFile.cs | 385 ---- SevenZipWrapper/ArchiveFileCallback.cs | 57 - SevenZipWrapper/ArchiveStreamCallback.cs | 45 - SevenZipWrapper/ArchiveStreamsCallback.cs | 58 - SevenZipWrapper/Formats.cs | 112 - SevenZipWrapper/IArchiveExtractCallback.cs | 23 - SevenZipWrapper/Kernel32Dll.cs | 20 - SevenZipWrapper/Properties/AssemblyInfo.cs | 29 - .../Properties/SevenZipWrapper.rd.xml | 33 - SevenZipWrapper/SafeLibraryHandle.cs | 21 - SevenZipWrapper/SevenZipException.cs | 24 - SevenZipWrapper/SevenZipFormat.cs | 285 --- SevenZipWrapper/SevenZipHandle.cs | 68 - SevenZipWrapper/SevenZipInterface.cs | 455 ---- SevenZipWrapper/SevenZipWrapper.csproj | 159 -- SevenZipWrapper/ZipEntry.cs | 119 -- src/Files/Files.csproj | 6 +- .../Filesystem/StorageItems/ZipStorageFile.cs | 74 +- .../StorageItems/ZipStorageFolder.cs | 46 +- src/Files/Helpers/ZipHelpers.cs | 16 +- .../Previews/ArchivePreviewViewModel.cs | 12 +- 93 files changed, 16511 insertions(+), 1991 deletions(-) create mode 100644 SevenZip/7z.dll create mode 100644 SevenZip/7z64.dll create mode 100644 SevenZip/ArchiveEmulationStreamProxy.cs create mode 100644 SevenZip/ArchiveExtractCallback.cs create mode 100644 SevenZip/ArchiveFileInfo.cs create mode 100644 SevenZip/ArchiveOpenCallback.cs create mode 100644 SevenZip/ArchiveProperty.cs create mode 100644 SevenZip/ArchiveUpdateCallback.cs create mode 100644 SevenZip/COM.cs create mode 100644 SevenZip/CallbackBase.cs create mode 100644 SevenZip/CompressionMode.cs create mode 100644 SevenZip/EventArguments/ExtractFileCallback.cs create mode 100644 SevenZip/EventArguments/ExtractFileCallbackArgs.cs create mode 100644 SevenZip/EventArguments/ExtractFileCallbackReason.cs create mode 100644 SevenZip/EventArguments/FileInfoEventArgs.cs create mode 100644 SevenZip/EventArguments/FileNameEventArgs.cs create mode 100644 SevenZip/EventArguments/FileOverwriteEventArgs.cs create mode 100644 SevenZip/EventArguments/ICancellable.cs create mode 100644 SevenZip/EventArguments/IntEventArgs.cs create mode 100644 SevenZip/EventArguments/OpenEventArgs.cs create mode 100644 SevenZip/EventArguments/PercentDoneEventArgs.cs create mode 100644 SevenZip/EventArguments/ProgressEventArgs.cs create mode 100644 SevenZip/EventSynchronizationStrategy.cs create mode 100644 SevenZip/Exceptions/CompressionFailedException.cs create mode 100644 SevenZip/Exceptions/ExtractionFailedException.cs create mode 100644 SevenZip/Exceptions/LzmaException.cs create mode 100644 SevenZip/Exceptions/SevenZipArchiveException.cs create mode 100644 SevenZip/Exceptions/SevenZipCompressionFailedException.cs create mode 100644 SevenZip/Exceptions/SevenZipException.cs create mode 100644 SevenZip/Exceptions/SevenZipExtractionFailedException.cs create mode 100644 SevenZip/Exceptions/SevenZipInvalidFileNamesException.cs create mode 100644 SevenZip/Exceptions/SevenZipLibraryException.cs create mode 100644 SevenZip/Exceptions/SevenZipSfxValidationException.cs create mode 100644 SevenZip/FileChecker.cs create mode 100644 SevenZip/Formats.cs create mode 100644 SevenZip/InternalCompressionMode.cs create mode 100644 SevenZip/LZMA/LzmaDecodeStream.cs create mode 100644 SevenZip/LZMA/LzmaEncodeStream.cs create mode 100644 SevenZip/LZMA/LzmaProgressCallback.cs create mode 100644 SevenZip/LibraryFeature.cs create mode 100644 SevenZip/LibraryManager.cs create mode 100644 SevenZip/NativeMethods.cs create mode 100644 SevenZip/Properties/AssemblyInfo.cs create mode 100644 SevenZip/SevenZip.csproj create mode 100644 SevenZip/SevenZip.csproj.DotSettings create mode 100644 SevenZip/SevenZip.snk create mode 100644 SevenZip/SevenZipBase.cs create mode 100644 SevenZip/SevenZipCompressor.cs create mode 100644 SevenZip/SevenZipCompressorAsynchronous.cs create mode 100644 SevenZip/SevenZipExtractor.cs create mode 100644 SevenZip/SevenZipExtractorAsynchronous.cs create mode 100644 SevenZip/SevenZipSfx.cs create mode 100644 SevenZip/StreamWrappers.cs create mode 100644 SevenZip/UpdateData.cs create mode 100644 SevenZip/ZipEncryptionMethod.cs create mode 100644 SevenZip/gpl.txt create mode 100644 SevenZip/lgpl.txt create mode 100644 SevenZip/sdk/Common/CRC.cs create mode 100644 SevenZip/sdk/Common/InBuffer.cs create mode 100644 SevenZip/sdk/Common/OutBuffer.cs create mode 100644 SevenZip/sdk/Compress/LZ/IMatchFinder.cs create mode 100644 SevenZip/sdk/Compress/LZ/LzBinTree.cs create mode 100644 SevenZip/sdk/Compress/LZ/LzInWindow.cs create mode 100644 SevenZip/sdk/Compress/LZ/LzOutWindow.cs create mode 100644 SevenZip/sdk/Compress/LZMA/LzmaBase.cs create mode 100644 SevenZip/sdk/Compress/LZMA/LzmaDecoder.cs create mode 100644 SevenZip/sdk/Compress/LZMA/LzmaEncoder.cs create mode 100644 SevenZip/sdk/Compress/RangeCoder/RangeCoder.cs create mode 100644 SevenZip/sdk/Compress/RangeCoder/RangeCoderBit.cs create mode 100644 SevenZip/sdk/Compress/RangeCoder/RangeCoderBitTree.cs create mode 100644 SevenZip/sdk/ICoder.cs delete mode 100644 SevenZipWrapper/ArchiveFile.cs delete mode 100644 SevenZipWrapper/ArchiveFileCallback.cs delete mode 100644 SevenZipWrapper/ArchiveStreamCallback.cs delete mode 100644 SevenZipWrapper/ArchiveStreamsCallback.cs delete mode 100644 SevenZipWrapper/Formats.cs delete mode 100644 SevenZipWrapper/IArchiveExtractCallback.cs delete mode 100644 SevenZipWrapper/Kernel32Dll.cs delete mode 100644 SevenZipWrapper/Properties/AssemblyInfo.cs delete mode 100644 SevenZipWrapper/Properties/SevenZipWrapper.rd.xml delete mode 100644 SevenZipWrapper/SafeLibraryHandle.cs delete mode 100644 SevenZipWrapper/SevenZipException.cs delete mode 100644 SevenZipWrapper/SevenZipFormat.cs delete mode 100644 SevenZipWrapper/SevenZipHandle.cs delete mode 100644 SevenZipWrapper/SevenZipInterface.cs delete mode 100644 SevenZipWrapper/SevenZipWrapper.csproj delete mode 100644 SevenZipWrapper/ZipEntry.cs diff --git a/Files.sln b/Files.sln index 8e0cbb1f766a..b911f81b9cd8 100644 --- a/Files.sln +++ b/Files.sln @@ -26,7 +26,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A74DCE98-A74 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{481DE2EA-E6CE-4A9C-A220-3B543B95AAA1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SevenZipWrapper", "SevenZipWrapper\SevenZipWrapper.csproj", "{FC45A0D3-7AEA-473A-B5E0-716A3814F331}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SevenZip", "SevenZip\SevenZip.csproj", "{C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -35,6 +35,16 @@ Global Debug|arm64 = Debug|arm64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + LiteDebug|Any CPU = LiteDebug|Any CPU + LiteDebug|ARM = LiteDebug|ARM + LiteDebug|arm64 = LiteDebug|arm64 + LiteDebug|x64 = LiteDebug|x64 + LiteDebug|x86 = LiteDebug|x86 + LiteRelease|Any CPU = LiteRelease|Any CPU + LiteRelease|ARM = LiteRelease|ARM + LiteRelease|arm64 = LiteRelease|arm64 + LiteRelease|x64 = LiteRelease|x64 + LiteRelease|x86 = LiteRelease|x86 Release|Any CPU = Release|Any CPU Release|ARM = Release|ARM Release|arm64 = Release|arm64 @@ -52,6 +62,26 @@ Global {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.Debug|x64.Build.0 = Debug|x64 {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.Debug|x86.ActiveCfg = Debug|x86 {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.Debug|x86.Build.0 = Debug|x86 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteDebug|Any CPU.ActiveCfg = Debug|Any CPU + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteDebug|Any CPU.Build.0 = Debug|Any CPU + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteDebug|ARM.ActiveCfg = Debug|Any CPU + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteDebug|ARM.Build.0 = Debug|Any CPU + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteDebug|arm64.ActiveCfg = Debug|arm64 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteDebug|arm64.Build.0 = Debug|arm64 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteDebug|x64.ActiveCfg = Debug|x64 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteDebug|x64.Build.0 = Debug|x64 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteDebug|x86.ActiveCfg = Debug|x86 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteDebug|x86.Build.0 = Debug|x86 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteRelease|Any CPU.ActiveCfg = Release|Any CPU + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteRelease|Any CPU.Build.0 = Release|Any CPU + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteRelease|ARM.ActiveCfg = Release|Any CPU + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteRelease|ARM.Build.0 = Release|Any CPU + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteRelease|arm64.ActiveCfg = Release|arm64 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteRelease|arm64.Build.0 = Release|arm64 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteRelease|x64.ActiveCfg = Release|x64 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteRelease|x64.Build.0 = Release|x64 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteRelease|x86.ActiveCfg = Release|x86 + {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.LiteRelease|x86.Build.0 = Release|x86 {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.Release|Any CPU.ActiveCfg = Release|Any CPU {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.Release|Any CPU.Build.0 = Release|Any CPU {533F9E86-EE0A-4FCB-B70C-F29532C1B787}.Release|ARM.ActiveCfg = Release|x86 @@ -74,6 +104,36 @@ Global {64C30C4E-A69A-411C-9F78-776E7AAD583C}.Debug|x86.ActiveCfg = Debug|x86 {64C30C4E-A69A-411C-9F78-776E7AAD583C}.Debug|x86.Build.0 = Debug|x86 {64C30C4E-A69A-411C-9F78-776E7AAD583C}.Debug|x86.Deploy.0 = Debug|x86 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|Any CPU.ActiveCfg = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|Any CPU.Build.0 = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|Any CPU.Deploy.0 = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|ARM.ActiveCfg = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|ARM.Build.0 = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|ARM.Deploy.0 = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|arm64.ActiveCfg = Debug|arm64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|arm64.Build.0 = Debug|arm64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|arm64.Deploy.0 = Debug|arm64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|x64.ActiveCfg = Debug|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|x64.Build.0 = Debug|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|x64.Deploy.0 = Debug|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|x86.ActiveCfg = Debug|x86 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|x86.Build.0 = Debug|x86 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteDebug|x86.Deploy.0 = Debug|x86 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|Any CPU.ActiveCfg = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|Any CPU.Build.0 = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|Any CPU.Deploy.0 = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|ARM.ActiveCfg = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|ARM.Build.0 = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|ARM.Deploy.0 = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|arm64.ActiveCfg = Release|arm64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|arm64.Build.0 = Release|arm64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|arm64.Deploy.0 = Release|arm64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|x64.ActiveCfg = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|x64.Build.0 = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|x64.Deploy.0 = Release|x64 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|x86.ActiveCfg = Release|x86 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|x86.Build.0 = Release|x86 + {64C30C4E-A69A-411C-9F78-776E7AAD583C}.LiteRelease|x86.Deploy.0 = Release|x86 {64C30C4E-A69A-411C-9F78-776E7AAD583C}.Release|Any CPU.ActiveCfg = Release|x86 {64C30C4E-A69A-411C-9F78-776E7AAD583C}.Release|Any CPU.Build.0 = Release|x86 {64C30C4E-A69A-411C-9F78-776E7AAD583C}.Release|ARM.ActiveCfg = Release|x86 @@ -103,6 +163,36 @@ Global {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.Debug|x86.ActiveCfg = Debug|x86 {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.Debug|x86.Build.0 = Debug|x86 {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.Debug|x86.Deploy.0 = Debug|x86 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|Any CPU.ActiveCfg = Release|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|Any CPU.Build.0 = Release|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|Any CPU.Deploy.0 = Release|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|ARM.ActiveCfg = Debug|ARM + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|ARM.Build.0 = Debug|ARM + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|ARM.Deploy.0 = Debug|ARM + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|arm64.ActiveCfg = Debug|arm64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|arm64.Build.0 = Debug|arm64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|arm64.Deploy.0 = Debug|arm64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|x64.ActiveCfg = Debug|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|x64.Build.0 = Debug|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|x64.Deploy.0 = Debug|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|x86.ActiveCfg = Debug|x86 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|x86.Build.0 = Debug|x86 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteDebug|x86.Deploy.0 = Debug|x86 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|Any CPU.ActiveCfg = Release|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|Any CPU.Build.0 = Release|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|Any CPU.Deploy.0 = Release|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|ARM.ActiveCfg = Release|ARM + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|ARM.Build.0 = Release|ARM + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|ARM.Deploy.0 = Release|ARM + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|arm64.ActiveCfg = Release|arm64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|arm64.Build.0 = Release|arm64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|arm64.Deploy.0 = Release|arm64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|x64.ActiveCfg = Release|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|x64.Build.0 = Release|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|x64.Deploy.0 = Release|x64 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|x86.ActiveCfg = Release|x86 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|x86.Build.0 = Release|x86 + {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.LiteRelease|x86.Deploy.0 = Release|x86 {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.Release|Any CPU.ActiveCfg = Release|x86 {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.Release|Any CPU.Build.0 = Release|x86 {3B15596C-3DB9-4B58-B4C8-442D06A8C130}.Release|Any CPU.Deploy.0 = Release|x86 @@ -128,6 +218,26 @@ Global {0533133F-2559-4B53-A0FD-0970BC0E312E}.Debug|x64.Build.0 = Debug|Any CPU {0533133F-2559-4B53-A0FD-0970BC0E312E}.Debug|x86.ActiveCfg = Debug|Any CPU {0533133F-2559-4B53-A0FD-0970BC0E312E}.Debug|x86.Build.0 = Debug|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteDebug|Any CPU.ActiveCfg = Debug|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteDebug|Any CPU.Build.0 = Debug|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteDebug|ARM.ActiveCfg = Debug|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteDebug|ARM.Build.0 = Debug|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteDebug|arm64.ActiveCfg = Debug|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteDebug|arm64.Build.0 = Debug|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteDebug|x64.ActiveCfg = Debug|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteDebug|x64.Build.0 = Debug|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteDebug|x86.ActiveCfg = Debug|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteDebug|x86.Build.0 = Debug|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteRelease|Any CPU.ActiveCfg = Release|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteRelease|Any CPU.Build.0 = Release|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteRelease|ARM.ActiveCfg = Release|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteRelease|ARM.Build.0 = Release|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteRelease|arm64.ActiveCfg = Release|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteRelease|arm64.Build.0 = Release|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteRelease|x64.ActiveCfg = Release|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteRelease|x64.Build.0 = Release|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteRelease|x86.ActiveCfg = Release|Any CPU + {0533133F-2559-4B53-A0FD-0970BC0E312E}.LiteRelease|x86.Build.0 = Release|Any CPU {0533133F-2559-4B53-A0FD-0970BC0E312E}.Release|Any CPU.ActiveCfg = Release|Any CPU {0533133F-2559-4B53-A0FD-0970BC0E312E}.Release|Any CPU.Build.0 = Release|Any CPU {0533133F-2559-4B53-A0FD-0970BC0E312E}.Release|ARM.ActiveCfg = Release|Any CPU @@ -148,6 +258,26 @@ Global {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.Debug|x64.Build.0 = Debug|x64 {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.Debug|x86.ActiveCfg = Debug|x86 {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.Debug|x86.Build.0 = Debug|x86 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteDebug|Any CPU.ActiveCfg = Debug|Any CPU + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteDebug|Any CPU.Build.0 = Debug|Any CPU + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteDebug|ARM.ActiveCfg = Debug|ARM + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteDebug|ARM.Build.0 = Debug|ARM + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteDebug|arm64.ActiveCfg = Debug|arm64 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteDebug|arm64.Build.0 = Debug|arm64 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteDebug|x64.ActiveCfg = Debug|x64 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteDebug|x64.Build.0 = Debug|x64 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteDebug|x86.ActiveCfg = Debug|x86 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteDebug|x86.Build.0 = Debug|x86 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteRelease|Any CPU.ActiveCfg = Release|Any CPU + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteRelease|Any CPU.Build.0 = Release|Any CPU + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteRelease|ARM.ActiveCfg = Release|ARM + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteRelease|ARM.Build.0 = Release|ARM + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteRelease|arm64.ActiveCfg = Release|arm64 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteRelease|arm64.Build.0 = Release|arm64 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteRelease|x64.ActiveCfg = Release|x64 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteRelease|x64.Build.0 = Release|x64 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteRelease|x86.ActiveCfg = Release|x86 + {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.LiteRelease|x86.Build.0 = Release|x86 {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.Release|Any CPU.Build.0 = Release|Any CPU {3D19293F-0E3A-4946-ADE3-680A1E9123EC}.Release|ARM.ActiveCfg = Release|ARM @@ -167,6 +297,26 @@ Global {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Debug|x64.Build.0 = Debug|x64 {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Debug|x86.ActiveCfg = Debug|Win32 {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Debug|x86.Build.0 = Debug|Win32 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteDebug|Any CPU.ActiveCfg = Release|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteDebug|Any CPU.Build.0 = Release|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteDebug|ARM.ActiveCfg = Release|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteDebug|ARM.Build.0 = Release|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteDebug|arm64.ActiveCfg = Debug|arm64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteDebug|arm64.Build.0 = Debug|arm64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteDebug|x64.ActiveCfg = Debug|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteDebug|x64.Build.0 = Debug|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteDebug|x86.ActiveCfg = Debug|Win32 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteDebug|x86.Build.0 = Debug|Win32 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteRelease|Any CPU.ActiveCfg = Release|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteRelease|Any CPU.Build.0 = Release|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteRelease|ARM.ActiveCfg = Release|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteRelease|ARM.Build.0 = Release|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteRelease|arm64.ActiveCfg = Release|arm64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteRelease|arm64.Build.0 = Release|arm64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteRelease|x64.ActiveCfg = Release|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteRelease|x64.Build.0 = Release|x64 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteRelease|x86.ActiveCfg = Release|Win32 + {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.LiteRelease|x86.Build.0 = Release|Win32 {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Release|Any CPU.ActiveCfg = Release|Win32 {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Release|ARM.ActiveCfg = Release|Win32 {A2FF3F3B-8EBC-4108-B99D-1476B7876656}.Release|arm64.ActiveCfg = Release|arm64 @@ -195,26 +345,46 @@ Global {B7E43D1F-AC8B-4958-95D5-A06CDD1FEACD}.Release|x64.Build.0 = Release|Any CPU {B7E43D1F-AC8B-4958-95D5-A06CDD1FEACD}.Release|x86.ActiveCfg = Release|Any CPU {B7E43D1F-AC8B-4958-95D5-A06CDD1FEACD}.Release|x86.Build.0 = Release|Any CPU - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|ARM.ActiveCfg = Debug|ARM - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|ARM.Build.0 = Debug|ARM - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|arm64.ActiveCfg = Debug|ARM64 - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|arm64.Build.0 = Debug|ARM64 - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|x64.ActiveCfg = Debug|x64 - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|x64.Build.0 = Debug|x64 - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|x86.ActiveCfg = Debug|x86 - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Debug|x86.Build.0 = Debug|x86 - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|Any CPU.Build.0 = Release|Any CPU - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|ARM.ActiveCfg = Release|ARM - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|ARM.Build.0 = Release|ARM - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|arm64.ActiveCfg = Release|ARM64 - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|arm64.Build.0 = Release|ARM64 - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|x64.ActiveCfg = Release|x64 - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|x64.Build.0 = Release|x64 - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|x86.ActiveCfg = Release|x86 - {FC45A0D3-7AEA-473A-B5E0-716A3814F331}.Release|x86.Build.0 = Release|x86 + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Debug|ARM.ActiveCfg = Debug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Debug|ARM.Build.0 = Debug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Debug|arm64.ActiveCfg = Debug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Debug|arm64.Build.0 = Debug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Debug|x64.Build.0 = Debug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Debug|x86.Build.0 = Debug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteDebug|Any CPU.ActiveCfg = LiteDebug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteDebug|Any CPU.Build.0 = LiteDebug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteDebug|ARM.ActiveCfg = LiteDebug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteDebug|ARM.Build.0 = LiteDebug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteDebug|arm64.ActiveCfg = LiteDebug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteDebug|arm64.Build.0 = LiteDebug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteDebug|x64.ActiveCfg = LiteDebug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteDebug|x64.Build.0 = LiteDebug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteDebug|x86.ActiveCfg = LiteDebug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteDebug|x86.Build.0 = LiteDebug|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteRelease|Any CPU.ActiveCfg = LiteRelease|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteRelease|Any CPU.Build.0 = LiteRelease|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteRelease|ARM.ActiveCfg = LiteRelease|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteRelease|ARM.Build.0 = LiteRelease|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteRelease|arm64.ActiveCfg = LiteRelease|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteRelease|arm64.Build.0 = LiteRelease|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteRelease|x64.ActiveCfg = LiteRelease|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteRelease|x64.Build.0 = LiteRelease|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteRelease|x86.ActiveCfg = LiteRelease|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.LiteRelease|x86.Build.0 = LiteRelease|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Release|Any CPU.Build.0 = Release|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Release|ARM.ActiveCfg = Release|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Release|ARM.Build.0 = Release|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Release|arm64.ActiveCfg = Release|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Release|arm64.Build.0 = Release|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Release|x64.ActiveCfg = Release|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Release|x64.Build.0 = Release|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Release|x86.ActiveCfg = Release|Any CPU + {C0CC2A3F-CB5B-46B4-AFE0-8F1AC03C24B8}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SevenZip/7z.dll b/SevenZip/7z.dll new file mode 100644 index 0000000000000000000000000000000000000000..d5ff365f96107a18c875cfe3f946e9f18b1cb053 GIT binary patch literal 1149952 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%0AXGQ5UKK4c8#v;!Lt( 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≧ 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%0A?q^|v}F6U4p! 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 z�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

6+Nz(I*;By(c3S2<3RRj>PO7@ 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≈E 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%0D7idnpqy=hGMswF0m=nt1|y&A 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�eaOT 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

*-Tfj$SO$k{cY>u05pJJz}m)?+l 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�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

o}1 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

dL0C4~m<;i-|mCB729UVM)-fg$7;*tjxqR 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�$mqOS3Deo0NvXxKPHLeHjthU~q17hfgq4D7R|G{YR^DqRNUs#1V7 zg1iECM~kq0F@EZ%yVyDWX5+0|QlY3M!M?fMwt7WgY%ZZ33IRTf)R~sVOb{phB(d%Q zer5GqROO<3l?-dO_NBObJ*yoi;Y~jhH@Mgv5VTG`af{nr5%;2%Vt|!CB6m2)ffE;6 zeA^BHYe1C04qSs{uKrZh0>D;O=jxS@P*C~yOV^*TGywD8ngrG(Wtx}2qtv^H%wa^T zln;w*8Yi;?C(2^FA2)8BQ6xD<^XY_@iO#|LXi0h-qkkEs?x(+kdIUcxfW2MW0seTD+y-?goh{?nHd~P8S9vei6gsD$g zNGnW(Ev4~eR(U2c!2q_5M$vxA=PG?Acow4jipAKwDf6SX0+(UseqZQobkzjNPofio zDO9zijLoH_VtY+X7i-v#poxdFA5H3A$9D=234roI;s|ccB_cxTv0?)(fZqYp7er9D zkjlPBD|e8{HEx|xCYI}@xnf!pg9}dJtfEmZ7hP%q>gpZo$v6QZE>c@}a29YpcMNe0 zz$*XO9J_gFM$=-+C7pu`?+bR9q1xN`m*m7PwclPyTV@QO+DdRBxI=NFbM#b@>1t69+Mg>88qCm&9Gmdk)gAL%OIy*sr%rts(^U4_ti#Xt0p z2KLc2I0hI#gnChND~OjBe105linZOXW^Iday}ThnZ+aCc;zZj<1p7kE${-l3&`o(D zp_uSUAYyE-aPcsKyvpAF@}d9E zd$F|xR>RW~jrCMp=XzFlXx$#<&^RiOVOK5=Y;P3tWbND;Jbk^}FigVPG!UDy`|3w) zcPYG5+!oXZ#30^fn0X%qVVRT{ORd+@!dmlUcWpONTP&?`_r+sgVNa~>h3!Vd9VDK9 zgj@;Bcx}5yxQy2?9_KP%{P4xrCr_}c@Q-a^>Ktq#ea?I3^>1d= zSaq`aar3oDE+&W!cTNPM-Hn9KW}kBf`}%!(Um@b^huDY;L9WJXTnzHgW4U*en{O*@KKM8DwD6IM=tR&&raZ52b8`NCh>>pVQT|@ram_2`j89HC& zJ3kv;I~~;@s*bAW!uG=`_0YUlTuy;;_zv!5K_p#JI4SiY2MZ3wi>*I`NqvQ-D@peP zY>*{!bd}#l4#as?CQgc@EjCuWv~G&4eILOz{^+pCKO$h6bUlmvw%p;Uvw7Q^WH9d^ zAJM#DJIwp_BbxW_j>E)vcOJ>iu_@9p_+*~6&02u#2sdupfZ-`2OCA> z%{;}RJ$9>7KE(IK)P;9>@6nxUUUGWK zq4O2L4g7JCocifUr^{})LV-cWlw;}~qMgPgp^jhUH!$<$20sK!%yl>}gl@$PVIphi zX`D?$xdFuxZUOPTRzIm}zRPt1exIs_hIxPC>W@l`qmBNL4Ye+46dLThs9n1+A7o9b zizyaepH@lh`@v;>Kdfba57wO>#8^m?*@_V?!J=wCr7(`<7`akZ$QWLIL|ThhD6g<@P{cVDlsGV@T@a(8`j z$f6!rop8Q|%@pa4ms>x8d*(X4x(<@n`ys1V=8}6>yRuSJ8Z7PA+Lm?#HUya|P}VsJ zKiekPz@rcvAS;ACrhKbx1fNFJ`IyT3k`R$yh534wT}vxfXjefr?s~1~2X7BAzS`yC znK|?`Klhn8*iJfm1s{303t4&%Yn}bkUx)5ow$>RRkWRRi6e#GGbY7|s3Z0OSkH*2# znfsnpEvaKxipJ*?-kl4fZYJY~yAyCh1FtB-u^!L%9GxAyZ_guN(*fvUF`cOfJCCI0L>97$$Gv}!1mp8#q#)Dw0XvNYET%J}*3Z;yn{dhz?9|KZ5>m_59{b*&BW zLj4dbFaS`S-0mg>hUX=-JuL3ovJZ4hBd^|9U8e>P!mG&7=7~MW^7E+Pmer-sb-Y3U z%@;ZK!OV{!Rb#mgNO_cX+*gBly|c6a*_)<2u-3Wrw}-*aY`GoC#0W9^wbs2C!=ux8 zP50t)tfFWNEVD1pIyU~k*u9C_Q*(}RasLQ11vkBeWV8u(y_S!H7XTtaP_m#z5i|$q z`qET6W@VU;8wPTFlyMxuNXmT`1Ru+_*FzO2R29}-$NiL(W1doq5aowqimwr7WY9Kq zRQBoea}atY^-J}V<31Wb<2Px}Wsbu#{=l-u9HN zt8N$Aqs(}0t_BLta!XTr_Anet!c;x5aR*lg?IKA}mBsU{W0TSl9*5yonE6xJl^>Wc zxG{~AuebBVcG!Q@ANEe{h56%JL8ULlbyJQ^$6J25*af$)wVfT>Z%dbJut1KN0b6IN z8cys-cc@CPlPMp0atHkSIY-v(0E@d~SZPT}}zO9|U7-IFccW>h~BNW8`p=0448EAfxbUtw78f`h&sEbW2Gdb<^b1x)7%9wq&%cc4R0KujN=?Y=E&TjB#%<}F z=1_C&4i*{xrnE1c&OwtvAD{*@EH~-AOiC@M;HTa^yB1X9#I`sIuR>_Y{ZIeaS2Ykn zd4GvV@X5Dv5QU?UZ8%mf(cdfjuDP|~O_^tiLMo(99d%(U%{!G8nK#HArfBFK-U03- zB(?@dx%AB}jw%YKhlJjA3y`i{ZU@6+TEWw~u&bYP+Z$>ef-6dbY2K2YvJc9mLau4T zm@*MSN8g4c`39T;Y-AElu}=hrpV9yp&DDiZx1ZLx_p9lC5qug2`banICtIZSqwM<^E%PGogLT#9m%yrJUaep~b5xTL7x7@8 zZv(k2LB!oHQnP&2a;p!Zi6b;lDM=gkFH|O4xM3pfAp(08r4YsCRVA_uPRNoQ>hOaU zHq`Fy=$pta!LUc;VSGgVP_?nLTJq^6ps+H({8VEowdLPVF`Q!aFHZR5OEv-h9BBGY zoj{Ov;hKKYSP-0*kG%!|-5{hvUwr?`o-AC2ndMrk*Uu-n6hogf`zmGyfmvYXD$(e; zAdb}@ltJ|hGSOnNIe`T|+53$fB<3h|sK@cYEBT0|W8|O5cI527E)>`E$%P+X1+{ai z+hMIq zu#uqw74((@O(_yuoe;+vnb2m7Wy|F-UM@o1R;gP6M|L$ znR~MNz1MEJ3;hn3Zl8IJ6f}OzbXMqm>yvgX#-fM{y992zLJ^xePS1YiX zBk#vwDmt=xcRndu!YUU_9SLg)e6KG*Ry|YmxfkWneeVtOK5ep5n}>=p+* z_S3A<0P+umIb7Tmhhb{=`uqGu&^bWtl~FQ!E==%ZZIt;s@b)|^mQSH?6Ziu3a)b_` zUc`4vm}Np8W%>7+-Q(;&FPhTBa=DX_N%_=V(Jup#i%V}D3SLDd_USafD+0Dl2gd;u^2g`9LI4sPFqn7} zd$LaI?5eLSb4gP$(t?SO8|0u?phi*m=@-v;f3Tct)pd`*3#|Z=^PCimf|7>11&q?t z;7ZA_8n~^io9*g;G!AUj2IUPN!aFL;rNUV zd^HRdhhn563$99N|HHfhRk9NZ2++~VeBnU&IKZ|M5mv~S51HEm%N?Ol_VPcofq+HE z!m16YZKRo6&Zr3(?VG^F?NorIro0t@`y3=OC~Mp}r)~>`H35L`#$|Nl|5FSgKM~Dg zX`AIJkg6m0ZsIY+!eV(~6hJ`F2F+>(u2oHR77UOYHiJWzDivEC zsLOB~W=4Fhs`_Tho2%wBs&X=d_G)Zb>HbRcxlcJI<8<&`Y#k}Ol+Xk*iNe5!T>y{g z4RCf|qS?XN477c+_723gu*=V-A)N1UVSM=l7~Cl1VLBnT7pBb{T zaChw&5-!B$lTJBMB)$U=AqYmwhlWfMv_AzI(S#8HfFBZ{_**=e!7m67&8H+QlVBR$ zf`d|`^jBD=aJ1t~FyFY*{TT9O$tEIORpWhLjkq?UCG;5jV~HsRGWZ2gf)RX`#A2+l zAS6&L4j~+uOz+W8KbaF4M?OIaT1;cqoo9h~G!&9F3xkwDEFXiLO)NdQg$5U%IHB1T z!9Pl20q^H&2p7lwOh}CkO!u-R2nE0G?Rd}iVvXW_vG2uWQ1-OwnU~_3Ur)CW7LW7;%!;dg7CrY0 zCI&Qk`|fz*fPVzK$;y^R5B?|~{P0{Vul3Kp7SA2L+df@LTiC6P7|at80m?4gPJncJiQHv zx%}(uEM>NI929byBK5&JwPdL$Xbxd+-&_gGfE8$YpNysC;k`74hIP1gXa% zdhMs0Z?STiU5mNAdRh7E4~t%X_seg+izDyw;DkdXRm6r?gF5wcA1bKN6)(KZKZ@x< z9}wP#^^zPVzw*sji>A9T6u}n{(ENvmG(Qw)-u0dDo4lm*%Apzc!rfgw4)hy-;Pu@F zubUkfoL|qoXj?49dZ6d89$3?>g=2xLe%?EJ=Y16V=UpPSz3KqLt1Y-MC8SNB-K+WW zH$Zh2y6GfLZp8TaLH4Y2pzjxP<;jQBp8H$x-kl!);lbC6YnQ#ionPeM+bj3#n>~qE z>U=MJY8mfTo~fj~RBV!t18wKnpx{P8O_MapEfGfz&2@GAv$M72NYvA=+_S>_X({D* zK){L+Oeyzd=2?h!rSh?6c_EhbJBF0^4-Q>h@CAKm$!nx=8tHs|D70|t&=c8D@#8p8 zhorPcd|X@&#fhm}z@BZ%0oF79muC7mcq(ZY5PD$8UFqxSe#36QT(A&FfAS_mEa2lp z2CvlLrlEz)Hp(vigTA*4T1yWl@>C-GGZ)uh@bSll6;DCx`BZ|Dk|V};XYlC;`{J8D zIBVxz+8g@d3M9A=y4P@T9!fkdgQh*OV=NyC)4#(su){bVhULS&Vm%ByLb_7f9s3Rn@baQR z=~Ab8u?O3q-ud^1>O65m?tH*s6ufgSPQ=F3?WaZHQjE$O^?%dwW8fY9lnQX&g{C${CN1YImp_PzCiUmmyqX4(i{=;kXbc6mmmI zf*USJ#UKt3lB68Z8pqMoOw1pEcr(TaF!kY;=6b7bX=R^V-(_t;mI)2Fy}jgkhteF4 zN~2`7^kHbDR!+=c7L>89P2hQNXKQQc(}e$bQk>9&qPW_f6CYwdc#XSNG^m^HUCH!r zkFjg3LDxtmVGomG;9{p$6ABkaJtIiaQr8BuhDvb%1+q#_1ye`3Y%TRl)AfiuDeY2s zyFzgS3R!d5+0+MsmDjK33E-(BFa`Y^1}_@AU&qM+0}v`!Qpd?Vg4&n?5MbNv(l7v9M{AFxQL;ZDQ>J8{$VQRA0 z10j?;U8Ts`1tI`R1eKwl2mU6=ihuha{swdsf9sY!1Am&G1h!#k-N55^!>oCvVjQRn zqjP5`4+Kk-()E1LgL&|fKS?U3-k-f6X~0cMEBI~qh5V68hCjY49r+f?bS~6t*j0xk zc&3gOu?Wu22+Uu{Q|f#KtVY+OtRUJLAU)sRA&^cux!?2v;tJ+z(L@yfr=^YkWd6%O zOyoQ7|9y85N5T>)WQRSM|NF!9`u~}){3^Sg{=e^V8!J-sUVH~1d?%|maZS=AQ}MVq z;*lZ2DmrlP?%>!!9&ifqpiqOdf(uj0gVi#GCV;PXzUVN3@h*vlx{@jtVWSyD3=gA= zNoH|$c1a7FWMU@q5 zg|%podDFpu8PvJySIbmX=4i`Lik-CZYR*gkY!>*5j3g3Fs|>2D+We(>33EwsYcl^M z?8Imk&m+uG*Q=`Xy^_TcrLsWU#qcVs2zP>MDE4gr3B3|rNRxfVtaG11_t z5xr17Ab>6q*^-x2Cu{UMBVDf zM-Zrx5`W|52B`EwfutzCq^M3GVhlg!ApXYF(NF*R^ch}^@6Qf|65}f<+>NLI`JV`) zk^CqA438x$z;BL9k`~4ToGIv*5JdAEZyr70 zzKlPKbJgH11%%9Cf!PbQsC}?_5$Fk&2rCDGM^NmuIw7xdz#zz0!T}ATt96DTe8^UK z;n6XHIV9+tV-w+-!IhCAa$hKrWMdbRj1dD5;3syTHSlEGNx5U+#r3oAr{Ty$4AvW6 zUAErAzWGsGgw=Kzy9H^WoF(Sip%wRyb3tXjKe;6ou41z?6qR$kZG%?YMu**MdTgEe zY4%$ZI%NRAXG9291Y!)rt{q96s@^hdT;lu8Hur1sD#bVz3tj6v$J5X4*dEasy;@ z_n>y)FrDxMBd=<~_k!IA012idq-Lj$N@^I5ROA$%VR%eycMul<*|@%c`42bTIGms| z+&Se6{%cNC=mFaNmSq$8BsvoiQb~4&cxWr{6o3==rtq~!7A4PnqdFQnWt^t|ZBi3#D4~>ytEDQr{#Ss=8Cyis40y6 zIr6O2^-=AfuW-%lgy}AyM6!gN&!z!0JS?msGe<1GyHxRNb!%w~)S3ahz;CP@>hvNX zecQ@$=kyn}bjBg?L5lp1pME5iHYxZ`EMKfuhKmvVH^}yJY`R$*t@yic`N`8C;wP?H z_AAe&4y#=C#cs}W8}uX_shC6;OHaCeNL4O)|6UO&l#~a2DO0zv0ia! zXt2dH?FfGgK80zPeq*^J%dM9IVb}aBn3lD3AU;fk_rQ}!XvL8FBr}YP@lN%LE$>XL z`tWD7sknhIQKR_A_}wwSD(P6)#VRqD7MDd>ak_drXZOUv6Pk>&OaHpckfwn6K-C~k1PG7tqz2=Y zOH*W}4E5%`8L)4e)2I_jH#{Y zaHB881r`eBe-I?FVV|k@j`6pCG>JDCKaTaqf{=(P@hNjVQ3oxxPc+-)Wpn@V=RQ@0_#gi5hgeSxwgxI{IW?EirIq3^%#?YL-AERvX`PwOZ{&+f<2itGW1GlSC;@(DElJB6#QmXvb%h}bTM#RSXyIk} zs&Z2b19Re!ItZ$0bKVkv>>oNdw(K9~Wv09auIS@8FNNMV6p z7mdHz3Wfo%RAPXrVn`()lm{rf`S;y1L#jy#I1G%NyR{~8xx%c0c)Sgvw-v2eNY z@0`U;56!|$A^xBQU6U{?KbOXA&0GGxIDNaXme$^y9I(vizz!dnI-XUI9^tW!hl6VD%)H(5Tn#j9UCk_n{!QyIu5!(j^=PTt&34} zXPXI9mLx69Q@K}`qptwl(xiQZq1>9zI*T6l(10Z zXr_s+T-z$m^WR?KLM7{~(6>r^`|qZ5Ig+QJ{wmD8@vpGAO-mHY;7sMTUM;JaF_x$V zDheq6hhM9A!{_s&R>Uf$yeX^_dk`0|$)Y(Pe<)9&#kMVg z5CmJMgu3sg;Z=m{LlyvfDhYu5S+L1b*C{qcjmI+AH-DW@i6aGGKIE`a4V(U{s5Nh@eQTbhEi?r0V`(CgoJ zV2K@Y)_iFPq{>CFf7^j2cA!sogG1j+=L)Af3!AQu{8DBUX7wij+0fb9PmT)&j)yz& zY`79n#p1owr~M_mzm55SGseCeW6apsZ-!VcX}UGS!f#fXlNDzEd^S8DU}`N$c=F}G zdih+~J>EPrZL)Nen@xj{SIDYhYL2uq4WKnKyo6eNl;MdKgC}W5-OD^c6iT+h7`o3J z3&2v%q4G1myR~!leCLnKaNF43-rdQrg()>kV*c?zor6E_ zxXYdpS=3;Jvhx_Z-yZE!S8Fu|Pr}#oMb|-dsWJ(ekvDRsZuiw@%gP_{hRPZ8wIqN4 z$_>K#0+_c=&nH83#;cyS4>p&|v_Q~Vu*3wTP3 z5wtX#-ZT1;>;w{dcYp~7r4@pGuI{(E6OII34>DH`V)j4w`fkvFxLOKp1#-EzG&skw)=K!Mo~%C?-U8 zAIEVbF5r!(I+c$tjGkk|vMQCkf4-GB9+X9~LKMfN5GAv?eU{d|#C1zrhtlxut6d+Q zGv9sa67sJq+JNKF0B*X3X5zMS@ZbOYe|H_FM_Z->DbNo41~F-U?}o2nn!xQ}MbZ8T&xtr=7oCum|dqhn#Y6vTiv-eaDwKq?!G z1MZTCl|fb&XF%)3PE`jP>0xN zxY}!_cBOhYy=N$NY$J&cMRs~(DKK4EWemfv*--&yRZULAl-;0bK=#NXx zjr;hgZdIEKR<>IyyojB2o9Wp49Y?k`L}4+AH-?~+uq+crAe9TZ$YJzwphM{$oi!O% zWbTc}1n^>c`P%M+7}r3GlJ2L*MqZ}f|D`yTt>&PElKSJS9a{-)tNfmBX7fUFF3s6` z+5n+$+4QOcFIc`G(g9jcRz3(M6Z;dHtl&>U-rO^~4We_XT3u?%IGg&T)xpWDtr|dF z{w%m-7#yoT!FOQ2w@va{fs4Y{yc>3LL z7OBHaDGubB1<_an)H?~bYTqn=V(|shnE#8L+!>YKZg6!+woO}$%lGlqw)(np;o=nD zS0FInG|V!9&J&~-_5h~fw94CAEI#{HZ9w^Qb|8PGideqwK;s>#P-R?Bz%-UQrQgW! zNdChBw82K~Pp=l74rm{>s#z$n8^quRt;hJ;CT<5BLb)AK1E$edANo*^GCSJB0wRoWY^fPW>E$kDAZn@#U!MWLNSvLuq1K55jTk0IIR2you0H@l_HK5TQ_xWUYity8@WU z1?E4!#UhXdv^ds^?U|4!?dJFbd4*)?C8O)`R=K3|ZadmPHbRhin0eF=Y^h7jzE7@f zDmIEGv{uI=F>n{_P0p}=7A66}oJ_7`x#VfLjL zagTPiirRP0Qm0`$3Z_#(3gUSR|70slFXj>E*WKs?;K4{cUP8mI&~ha@QWgAcZcANy zIL~Rly#FTUt!$l~(rn;Z%(q)ZN8<(S6!v0AOUGP6UMD$hpwT#S7q*>5w^jQ#i?$E0 zYLCq6m6nwgW%G|w4|=IGt=^K+R*b}UTkNuX8KznnQP|Ng*XxpASlHcd3;=NUrx>vd zn^T`TsQ=3)oRbBC@Fycs$%U+(_(`X$0Dlc$-obduym{IJUPCEi-ZrA%GG`(BkW<-T zZC`g4a>_Xl=r;ZcRc~~iyEyS5xSTg_ZSRE-R;^Lo zu&j1SNRNq^&hh#g>)Rw5O&Pin!6ymlntsgL*s?34kdILuJ;!1?h>)Sz0yOV~0(fnpp+SOd$_h8c$dGZ8@G8Ur9JpQK?)GoHu=ol~r54DRi>3FdFq*bT0XT2w zC|naEQpIF0=b1N!W19+Ub|OuyG_6_$6f8*`nVmeKT33vt_|EhG7S9oX!%siv7a-MH z7dtITF9as~U%|}-=KV}iA;l7HE=Q(L$JF*6Pn1m7FO25Tvlto}jxb|?9DsQWQn17$ zh4<8q!A_f(H;ijJuBn)u>zHF_a>8m0rl*5bNwp+Dtc7h=&|eM-wuF2f9t zMK$RV@+6#00+jpojWS@#Wl}Vi=(pZHyN*=`nL1zp^lya@5F{xG>7Ts6#G`95x3_T+ zg`9gWn zrD49KS#R*s_CDkP{WDtI{M!bM^!J^AJbPB2#dqHw2(k%fX6rWq>^TufcH_xONDd#t zB|mvjyg$&OTM#>YR-|zC2v01ZyYTP4LHId|B(z7SA%*$NRAq| zkUI2Xs_)_|Fd~p9Z-u!8hoBu-P!%t-{{iY{73_FtC%h^lYtei32>jE?Rsfs}9bH;H zcr-Bq5V-wH7zz>P1_9qJks!Mu@k+1{SP3>sR%yOCNCK;T{=uFYXM$I?ZWu$fiRhm+ z_Y?VfwifitsQpm?T>L-n3+8)0VGnJ`GD8OZOr_AM!zUliW38Em62s#-NTa8j0?hOo zmglo({@ToM73?ku2|p6DSz~TyuGJX220<gc7+)(Z~bD(;vsMXO;ayA7vRc#xYM zL3!2}PHXW_?a{1djUvpQlu@X562E<{QoEbav>=t1n{7#qt^ELZew{wz^StauTDj$& zU8CQG&Nrd+IE9XLc4(@#GH8<5r_xH%I}^n!RtniLQA`pGu;u-$4&7775LyjC+h$3~ z$J?)twahu0+Sl^pv#BoO@^m0R@1fDo$j)EcPl=(=?;?n?#Ns~^n;;{J3R#<&T%Au~ z9*u*9@?-R1dVz4~z#jOP5Wv6^^8}rmigj*+>^g?d*w^uVItHIG_6xCg>33{}w6*C* zuO_$P2>0Zr%^qm$mzZ6LDOKp zwT%f~uLoMKc(k-&UXJ*uJt_S=WFKcMafKWN$%fUEz>0_Bc{b2=PnK!aQdV{2k~hj% z!9?1MbJkJZ)RG#u=%WQC3mQm)lJ(#uT8fnw?jGBK8Ad;ateW~)dd!T`U$na^zZUUY zlTE3Vflz(l^PNh=Gj>-djSC)9az5Q|WP7-0oYL)&|B)%jspAz`jb@9eaID;^MgRmt z7GX;0$WJSRt1AnN_R}Nr4&cdhQg{tFGE;&L}cT!5BT}hmE7bWkf zXTe=7>*gd~bDbo~9KmQjprURFv6mH-|uc^G%&+OmmZvM+5QV5!NKjneFQIk(O zFaOE&SY79jzt=0;5qcC5C>M#(c9d^BIxbm8#dK7u){3GH)Qawy2h%C{ z@3G@fBWyB!Q+!k?V`8VXd)a{VsH*?wY6Z^$?Sjz?Bju$G=u zF}M@lspUD=Q%07Xd)1Kb0<_}r*ltlMmEwR~yO~e~K?Rd~QAzs`dD~WXcs@9aqf#%k zYEQV9q`uYH)}Z?64UPZzrJ@7%l27R7&cue*6lQ0FSniREQ zumXZ86^0L#`ajs5nN};dJVc)0Wvi9a`V|_bYPbB=vzKhj+RUvrz!ru97vrFM!sSPi zsqa>#0v1+DAJ#USlO_vW-IyKA09BZSUpve8!Ff$Sh=(vZkh>lF zx~7$NcI6Om&g<0X8;n(N9_3|qh=f|8%bEB|bqaJ*Jyj=`+~tnfG<;vjaZ$&)%#;=< zB_OB;SNIP#IY`01PJ zdGgD0M#>oN9&LJtxku};TFujrX<5`gw0 zW`YZA5GeqjVv_-wm>5H@3S$LKk%FnAvRKGE08TH!3av%8T{4ruY3sBlF3RL0bm%({wyQ7M4bs-3u63F9fal zf?%ku?nt>m2UsDu=&q+Id5Ld(DWl@BYEE8W^_)n*$=M5 zNTRyoeai54Bs{yL$NMx8e|+`QS!`6$;yU~@) zSf``9TcFaDJ!w&JMkMP3U|&EBY^m@^jXFM7RCS$PXYO*-bBQ3dEcB)cJ>B1D?P8PM^Ot+ig zHT7}iFGS`Vi?P~ad?VjasGpV6N-7BRXmky3c@-6{c-`P+#Gi8e^kvgaBa$SK~EEf?7>;YI(t5ova z({D2vyzS0lf#p(1*PTr+7lWCeo}PYvT4JYC#3iJqayQfL-{;fyc$|9k_9N4|U)0P- zYJ=~SgT|I*qaE)DPWMLOuQrmH{whPJSCk>uSuCOvasl!ezzS=v_Y1<$6DH`fAopfjt+a~Ey77h~ShU+T^^#v1+Je}I?r&UkVw ztL%|Q@6poBw>9;?uw(J7V}S{?aKGXT%%PGjY9vf=#y6@wv^lm$=hBIz)Ue4G~+gr?R5x*n1>nmS_ln*Wccu8;477&xsBmo zR#gt=1=1syH*WcoPIetCtcN+;n)4bu&RQ|lTT7>xD#WCceOq(_>;PF|8c8QI{BS*k z4VFrYGQ!K(jkXooJE|`-A3s}GQ(mq5mc3U!8~B@2|EM$K#wvA5UN_3|bqiS3z<7}9 z^LjRWD+I!@gdOsZRP^q5P3qL~Eu==<#m2-Pk3+dL-Rlqe91f&n)DLG#@y&O(grgz2 z$T*L#F-wpJ!>cQfih^#}X@nQ=3hQ~YX*fuqaHsX1l`&W+oEp8hDA}(CeTb6EcJ)}2 z-mX1cTliC&T=yzqm8E%cN;tB6u%aKrf)lcxQkFCS4lb)3R2DcPyEl$a%TOaqWtjb| zFb}e50g4W$pl=e;3WJGr4tjcD6si^_t&3?PhQ?ISMu0$^OJwxqdV=3CC7a?>XZvxv zfidMZwapa-nk0yqs3~2*sr6w8Vbouws~Zf-e-33>`3AkfqJYPsQ%=oCKZjW=cYcP+ zBnbMBh=AiV&-t<#4L$=UUP|Me@EKKYwa?8z&^>?MF+;2JtI9)MK_Ax2vHR_uWAzox z)LPO}e`Rfm=Q`8Vu%Ks^~0M?qG;nj?CQ|ZAT1X*;A zohS_1KN1jgNin#*cSbY{IVi|%1Q{BY5`!<` zjW~%MSD<}Mh;@*x8Y}U4b^f!TQiRd50K_=Ty4hqvjal1Wd$6Qsoyo5p&1=w zyP2!7xC?bl8*EdAVQP}OU*%jD%nj#T1|IR6msH}Oh(qr~_;%Ts2PBez9irmN*Nf=v z()pm$=7>m;Qm}`x1T%A_#yI`rD!4ML09Os(0Jlu+Sg!jI$Q9kfrv8n@9$r65+`PLTRez56Qh=&al!QFS`$B3qb z&8jlYF2S6LtB|9kJ7i`6LqNR0gxV@XM%W$Fs^@X5**d-e4Lce5?ZS&CKd4DH2&vY5 zqy4;nI_9tqqg9Z~N?$%{S_JSnF=FwQ11X#-kI;c7GYqV;>tfD;iP+^ST_+A=dr@ z*^slGvre5PM`w?;u=J;v39QxR)sD`8X6lqYin&v2J5)9<~1_Y++m zj{ZNsZ?wxuim(ZzAX9Rgg(&7F3?@8#EYG>sTxCUajLP$aOIpN;Tz+pi!u!zPf}!tL zR)iS@1@bXT9#qPP@$9*-B3=9yQ;?$|Ds1Dhl2-YDmCMjp(#pHAe&IIEa&dSXRN3Yt z`&{ZSu1Lg9Nlw8poMd%c7Wo0{3Jw3Zj6l4FMyo`|$S0XMa_p4M4(GmKCvJ_nUX=a8 z4EQ?t5EE9xD>Ih|8sI2cCQ}%lT2x!zUZm>zGOjC7ZI?OHwn~>&IdMn%b(LN+ctcg^ zfy{|PtazEvC*BpXmzc%qYnXU2#gAxoOz~^0f}32iA9jb(nT>THo%#uSQ=Izobmw50 zh&)0`NqWy!JB=NtxinY%26x9XVN_)h%C1v!pV=Cj`)J?u9@1oYu!*X(^*t~50#4Ho zR*Q%BzE``ALaOvK9jvwODL>wF-bu$lm)*nkOx255cML>xJ4#x%?_LEA%F=d=n3B~e zeUkBUOuCvtk)imja=;kC{Z$6Wi^!keqGi@iHTkYYG|S$+JTwJgdgy*vlJnlH3^OafPDdfdr_6|AL2+a-=NAdbl);8TP9rI!C zvMN$vDi_j?|0%jz-ZZvWLFu`_sN0)eqy=c&ZA^q^7OAnrN1j=csuMIjm=HKVN7I>H z1wOiA@jj+MqH`DZE17z{^88yriv6oNq(W!Pz_GzM8w|>931o=(6v-q>`Lh9oQrUXcdSs{0z&;-3O z0)5rDmbR_>*N8Su)38!Qs};ju@9RMI5jrX;pT0reN`wwX+S&1WH72*!oLf74sfbWk zGD#9%``Bx>>uuv*XQ*dYN|GieZSZgB8c4Zih(ro;ABRcb|OfvNqiS8^4h$tMfWf1- zhR}}YQM*}GtE#Yj!-KdVmFsRVWx)EkkErCkn{18Z!2xWxom&KUagukK$?wNTgKgWO zjC)L`D;1zSUR;e@!`Y@j zfCePzB5can-ylS^c=z-@JzDO02UQ-O{*O-oN2mXz)Bn-w|LFAJ4;8=nb@u2rdGwk* zdQBd^CXZf|!9?nI0T$As(_7&GXgUoMloQdL!wi-(Wy^U|CP+O7VIG4p_ZNg|<^3UZ z2%OHsW1hidp27NoAM*@$DGFTD?@!~<&%j_nurpE>j8->>i4gcpgxYCM({BMuE4{nZ z@HT1`E^ZIw;J}#!C0V!7c`uTz>2s1}Ae{KW9b)Bjt#uc!bLx(^IqL`RFE$?Nk?+ll zuH_3ACAN3{f<5n@FlxF&9Fk&Pb$?bh-KK`?%8(s=bHbX7)??_>xOikFlacna$jRU{ zDrw)qY=v1D-~Q6)Wh0RZm&x}FW^F^~y{oP=$c!Z1g39vFeW3E*m0i}Te6$6b<)!;T z=F8|N1Z}$yr78F{MdR%zO3;nmO>T^A710eT_5h?y@K1V!VHO`|E|YN4F<7Fl`~hc`WY zOm5)AS1b5h4@RtFWmUI}yLtlGO?hx4#ezAvI?By5E}});?VXW?Iis7RfGT(##F574 z+C!AXkC?o?16FTPuHczo-dBH}j6KRO@aSK-Wu)8|F1Yo~_ncEAm5USrah0EtieMwsZS5%6IA zDa>{J!`VicHr%^GQaG)t)w)}~+5lDCGSX=h`UauDaOg=+I7d*Nvjg6CA@LI^pi87YqhueM~G{lp0#`wtMSV z9nnxnDswj{%;=>Xta#2X_E&fhw77W1QPj&SciUbG722va{B35!ZhYtwa73{wRxd3K z6T7c~tbR~9SR%Do!7;4tclfCY6Y|pfvD6Yt{;lis4G zGD5zsD8M~%OB!h^HT5KtZ9V-_{;{t*y^R)Kiw;yc6rP@vXMYFozvlWKP*?U^)o$6@ zZB*@<245jq91HLNPL=a*_VMpvbB3qx&P|{CC0*Xm+aS!iSVhAVaUdhb|9do1hTn~y z*bk5X=;1c(i?t2z)g^r&tSE3K6o|EhAYf(~XXE<9dpc6vStzZ>!JdR?#-P??yoCR`7h|M# zM;VcJ3JW*x)mX=SU2 zVfMTV^B{{BFlWq{x18*cX*|t2D;jtj7(sWarq8B&%<fsYG=K}`B^C?*0yDdat`i^RUh_ihf{(2DbE^OeGc#C ze3b<24nVCDN9!+_bA{S*iWhSn)7D?6AHx=$L`g55rh%3HZoQJei#QxIooffPwG%Vk z4W8bwPF=uL5H%J3L-5};17+m^JyG5q_7G8-6RM~7;Twme$(tlSpJ6>=6y|lD=8!-3 z6pmYc)>bV|Ksr>0SK?xb!2`XO=LXt!RRY)yJS?k?ce<;P>N`B^gC3mfYQ{YDqfXo9 z^fRI=xqGMjtiy$l@}Qkb@Bg?DbIc1V`RzS$M@yuB@*=U7?-ZxJ^Jo3B)v(*Ti z_=ls*3RHYbd!)&lHih7Btk}o+?Z;|uMc;R74*K=$O26CmyOLjxF{;fPM%CgFd~MUN zDM^v0ap)&&H?eAry7YLe?;|#GFf@C^x^_X2aNgz5sMtJx0a`R!#;Nz`K%n-57v695 z*WYc<=2V*i3WOJ|(imh$Kjx@(6jD~5;`k8?`QiU-njLoSBb(~ns<5ToAp4y&YUrh4 zPIAJ4GQR}R0-2ROtGysil;z_m%>9Qz+T9BT#LGikn)SX%r?=%Z0URVS*?!^^ENf0j) z6HcLGm-7-gWpQX|BHGez0g#BFmg|qYLYgfWwUUNX!KhDmopMh~s@u%8f|EKo*lg08X zjsk@RNVrq@>_JXYD8Ky+@^8=83wgk)!B`I<4V5ltzHv&scT(-ustl-~OTHdsn1>vXJCy(kJ-s-`5iv z9=@C7`8zmJ^MDa@3~RrI{ihEOU^)NrJu5#Ay4U{!nSUUdFlRX#uFYrQrBCp`rwm^& zEux-YAbX>%AS}Iu)p2s@{m0Y8mi0bSZ&D*5^?!S^~&)q3$hTJx;`Hq7bpnSN`0(J60Dy>v z%Q@i4hil&czg`&U;kwse0rP%bGeFm0y8h7idCODg;Q9hLAp1Qgfus zTdR0K!aWj)w)teQ!Wg>*aR5@e*o}r>4wl|lggg?raIND^IKIxlVX*!frL9A+wex{X43YG zseEB{ZoZN_g>V4m!VG+Hv(g?rwGpAZ(w+m9qq{V-MgKl0kbO{I7xG+*3>79pwql@v zLJCXLn65@vqYFtCk(IJMWhas1D%JSymneaip1i&QR>DSD7=OS9F4%yj zKCSlmxA}4l)0Z2}!|q^zTd#)}8b*0ZE@|{20r`=>?Ku z`h<18K>e=8_Cd19@@KWuysI>zgSopqee@=3+8>%RP+jVc?g_I* zi9$)_iZs6_SM;ol77pxf&tI4GEWJyV#1OR^=PNgh!g24>M!WlLl`l=wQZo@c#0E2T zympyN}NrHH|n@7Q%hoSYB*EYAsu*8T z7AOdm8p9_TLBAy{raFcSxEMZv<(xz%7sNop_*a;;!&$_G3oVt;MUG7^YkCSTl6`2t z=ulFzTOmdHqa=)4Q_glInug}J_nd3Q)lhmtTG{Ad&dt%WCQ~R6s%nbu#0ya}4L_Bv zfOnNkB+p=m7A6qUGsLCU$q+IEyQh8)v(!vlya9_nuc}{Tlmyqf>Sa1zjm25{8rB*? zk8Mo_Jt&Os2yvL>j`{2)m4@x;?jmQ!#0zgjw3}wAWEt)u-i*TuD#_1K3|BYF(tp}* zz?{SXI!*BtuwRbBddV_OQ4EM~O=WyXK7px9u{3-XX_hT9{JYJZwsVu9{fLfcs~HS? ze55+Que+}FMt!a&P7I*XY~1R%@U_bfqz>vEmQ$`j<`wFvoL-9s$XI(%i~3b^Sn)3? zsRM*~NnEsz5_gFJR|x;`Do^8O5z6b#c#`MG1(-6|b3%l{?+bSjTUCel1FXrR;@FfX z+L&WLVP%PpBZ+}7B`~hk33MI9{d0v~*~q&frNkqN?h5UDf}d^E+Ox?u9FEzA{@t?n z?Yf-a7b)oTidQ|0Zr;Hwqfe^tDHF!Tzm%6I{E(0bA6RalBO6i;^C;fBDY)q5Q^hLY?xZSh1q>4`HFvPEf>PSRbCo)nZ5w zhQs-jIMNab1E*L_+2cZ&(c#imLyPY1CoA4G4oKq*YnQiX1Da+;FpE9W6dlI8mVWoKn5ox&KltoQJ;Ww z5(%7Kxi-$Ab+>@mZy*NVZM-2_82i9FPYDPOgZY%)Ma+h#GyPG@_!=UYy@*;7Cs|DgK9{^O4XS40O@T*A7Rdavu#6oB7U)AP#qIw4_!ZKNz&uI7VC?Kw(4AFd1UKvz@r#L=? z9M(qWJTf(|tTK+RQfKx2KBtgFP(9a)8i2XpkS<`SZ9Ed4?pvSNnl;~l~?y%1F|OVfg{A0Vq~ ziRB@lBlxid7)mq31ZDp94q#gkH=o-rWmJu-_u!&w?CXv^H1(A~8sI$8t_|?ceR#CL zw)GqJ&^=;p(rArc?jjU_dyeN@AxrV5#&}kw38Z zWMJ%%_or>xAGL=EKmsw~Qsw$B=9~!S&+5s(_X8VZ0~8WansM-#s@vAEM3|XXd*J%Z{?{%mOYZ+uqo;Wgy4#?Oe~PHRRoj=h4_Z~|gRoLbl+Qz3#0x=HpaOR;LQ7{AZ<)dk6^Lb2 zfUiQ($o%OvR3+W0eKUVOFH4dm)|NT(Lp|Uc=X#rZvt@?*cd0aUaSp#fMf08}peHtB zD62whxb9CG3ALY9cwrz1(sizIReP|d#%XOzVfm9+5CWm(kD@u5g`EPZm7_b;&Q7xH zCB6cvb3S|TC)eTH#50aeqIsdF2D=xh@L0ylh0i|C;2+6xtxs*7oV5DJvL4sx3b;_6 z%goZ@7>7GjW$JhQyT%znoQqq(Q58%f-3UI9d-==h9pW$#@0!!`4!$9kv<B@F z8h8)&I%xZ(M_c!uf&6f!`~d?4W>=Wy2VoY+wty;sS29Ag=Kfd<%F;`t25pQrPPRma z4j*#v7~tI6rUlUl4RjjJd;JO26I#bI$3}3JAPD5TVa%5~Un^Xy(T>Kj;eI)fsc!(w z!HhGVkwXa-i-HiU5rLXX{S{{*sr=w0MnLjrIwOd)D9>SeaLxlzxn|3_GHrJ-A6CI+ zxdP^ZzO1A}nqNq<3SE*K=?9)jLB?XU$Ta&%eM7E5bEFJ!O2MR$d7|do4Q6%2CKfGiyH}j)-jBcHEHtds=`$0i`X)mYh`NIAR1L>WW4woLD z^$AuCYdhGTMnxpZHGdg%nOE<)#_@}Z0-)yRY^gVma@uwy_Jjuj6M=PZGK%gv8m6PD z+txZlxmA;~y3yQRCB`qv*Cyexi|wrX8mzbDcxP(XkVUy_6&Ox;pr8_L>QCXt9Io14 zx{To8V;mbh0C6xmL;tY{@~w!e!XrX;oJ*iVWbPzWwQZGW$(CzGAmM#n*iWDt8!K~d zqqC1I>mdUzm2Zr~nC=$|yqtlWu`E&yumqHw5tNl93VNwJk_LHbPr5iE;6`$+ax5P~ z<}-=sp~F{jBqwyM-Yjz}eozth&SB5i!hV$jnxgdeh(--sM=)<OZxF_6*u7DRBT`4P=@@`P?i3f~{1)M{+EC5bFb|@Lo7jTniNY*2`XbX(3G&_%nTK-&?!yK6 zXbbJ*nHXIwu!4c}ihQv|>YZq@{ z&mtS~cxs|xkayZjg0}{cDaGBJ$qzf_wn~@lk^573G5mufDrkXTkubNdE;1`*>qC!vDRsA>v5-5_6 zo(9Mr0SCrh`=u-rVPciJjSVrsEvTOiEd?M7hEF$q?g{fKIhnI4LscImJT3#S2l!h% z%r;lkiDafKIBNlG82B1!5&Sz5^{kcIqFJzJnw#j6Qf@<86xbUf`Za?ad zL^{im)RB0Z&wD^rwzme+1z1%hj?_+0b}8Rf!t!t5cVw4(MsJ6gq&VV;p){pd*k=r& zxL)P&e(oOB7G}?TQ(5FF9lscHg?KP*4=aARa#k@h2-&`t*tLENX2K%RB0p*?WK?sb z|480fsv5OR5}{0EnP47Qiw~wHKd|)dShq@RcvS`C8g&S?{1Qz4o*C3JI!sn76n4_s zwL(KNyB%7(DnnfvtoAh<15Z7`wlP-I{vfU;Y(4Z*cbI{DfK&`lVJlLHjLD%_vdmm8 z?aR9E(&`uC2ii%23msHm$1wy-DmeMAz7Iv;t#i>gQ z)~~C)IxJEQ&Gv59`jOo)3>2T!_Uef4CQx)`x7SLP%Z2sb_u9zcPG{*ymsZu-;nK{v zCcm_Jt7u3KVCYstHSn5eHDW05K!L5oxdU!aLG^qOo6f>rMz`RK|TI=umuh-M}i*4L(6li7Y#Grz6}_ zy_6u5QUZ7)oOn0UO~fE`4O1_rT#dPpQN&D;m3$8ivZ+v9fN(JUkpnddE3&wwvOIV@ z%=e%4m)bg74xgzk^uhWWb35#~sDwJi5NqFkFX4ftEv*oyu>}SNU19desk~9d!@s}t zubcWv!_u-o#!k*)>xr_pD0K@DX4W87J|qz$0o>PAt&h=6FcyQtGn-s)ni>^?REs=O`6R?v!Gp>HMnVyN)!wEa%cuJgXjhgXZl5W@ALXEog-mH7@hcF2QIvN|hVs$5-(q1n8HMUc>@Y|pVT$lVaDK&*8O}-Ynr4&c!+ztbp73Qc?cmqyi zJ(w58BLCCz@d90D;ZcCS&S&xJC<%+>yYLFOBR>xO9JGYx6qK{$lXvglo?pH`y8N%p zLjo5K8*ysCFgsu$bm|45 zs(%g>M$b@s9LOJL2of0L+O)n5qG2s} z64c^(Nm><^QZ|N`J-#z4R3w{B{+*lMz1%b0}hui0xAe1C?bd;UU=>Gpi#fuj&UL1MUDeu;FP16kcd*KC5dr;$l z`)e=#?^5{dDLvGqeKP&cogPdb`OTe9+_14ddR{9$yLINdqwCJxv?;t`bnRKAtqV7e zZrn6_&@m^Bo*S+|tDeibyT~$*Jy_F@OzGM=d$pcW<^DihIhsjLYTC(wnai+GZ3O=) zR*KkaedHI6x2DCe_OMRyv-1YDYt7p*DgGb3kwgF1Lb=CiT+Tu4TbFaf7a% z_%dh`{R8}*M9_ioVTo}4g0tRm0k{{OB-?c|Po1q(f4|hfq6naC-SSGI z2V>|N7rGIQN4{OrH!WHzO*=0D94$&o9*Wl^pMgF|c`&B5R7xv~wU<>oCxIVu_GVeSZ#{G_fyEgI0m6Z6ns!?iND9#kOr1pr)@6_+5?u>%NI zT+5z$9!l1Bl&|(O@{LhHYo70lc|gL**wun=v^N@>m9>QvLUEQ8o&&$GrSbrsP)0yq zX{u{ryPnKuLAEzV*)le@3&%I$;1!@Alk4&I_Gzk`kkX&Q)u{BQOm8nv|N4dLu^pC7 zZ^Z35TV<#NEusuXF%M$IK(^?RXOq~O+11%Un1GZ76~QDj=Od!f$zZU3CsffHM()M+D=yLKQ}yqd}fb0XYn_ z$LKMG{iGO)U;_-=7lt;a@Pqv^Onqo+Krjt>+QXRkTaYC&oz-In2V$z@y9)P%16VdJ z$oIBI`Ltz|hIVL*rUXwI?Z;D^6;vTc7@a=sL7kh?ejXymGMygq+b~>usCQoh3Bw;j z3x9I{;X40cK8R99k=w_?5+$R%#T5)&~_ zl>C-%ASXW?lVePeVVL0-sBW8URV^z7E^?nMYp+_)Qzy#TJkWOURHTAHU#7JIH6C?i zXuup`zVw5GxzAj!1aW|zrw2?E0%LqZu_Vs{&$D@G7fqn|uG)jct<3O*rN4fud zW&dNpVK4T^y*NU>h=6bgyxB_22}hA!P@Kr6!&|Vgb{F$ldOCb3gn~V=yKaIitA~hr zFM=~@Mx=uyAyg@+!w+KKmC3xErdP6nEz^p5Y;rZ@gut}!p7Srf@IrUjXsJHzn{&0h zm!(`y&P{XRg1GUFa03)tJ5v_gluR_lt`VLNv782rx5f>c8>&+rBj$0y1h0gOs!}*{ zM0mW0!;To-3V)NLL5+R)6^(Q)Pu5eCjD}NPwLcNi4Vvq?y4728R-}Ss_DI$Sny8gY z2(GGa`(cR0v4dA@YNXkOh~PX{aJbgZ#M|mwiPeZmHs1+Hkav*Em9+55vwdWnR!$Mm zze+poor=|`wRP8Nb?C7a&O!SCB!_K9?ag9oFrkOFT>e!RstQM1wg=gCttzY$*5bh|Fpzr?Os!_QcT1Dzq1d4pX7MF|?-&?SrAJ z3hj%bq9KuZO_U(hnnlj~0st-+!3@fSiXP6RyR#m?0o=j*2z&B|XN=woxXtDb@ZvP? zZmZ-?Ne*8PAkW;<;DNlaI+EM_ZrRSCXz`qZ@YliL?eOO%<`Vn|lcDyH0Q(zi>EOrl zhO*@;2>0R~65fhKi~AyDRrk4R9^lA)^ zI!R`s1i9Az5wrO2wj=TeE{&-7cb9ZfTwud2GaPY2DFI!YH>_%(>@i2^c5noW!4w%E zwrD@QFm(j>cTt3g;}YVvg5@gAibvqgdL(t8QTEGU6Q$YCN-fsN$7VmHP9}O6A>I0(MdW^6-3nICqr? zna_l}`cis$G@?3Hsdzg33(W#fWG=*QJ{G6Ael0i-+!35i-P*G_J`stlflV*gWw|%t z_&}ateL1c+#XRC4tfMLzld7W!Cs486oa#C3>cNQ#C^gB2sgN)$5;h_e-m51@W zYl_CQCxG04iOJp4b?v+^9Kr@7avxOF2g`KfOS)-7(sU=`CmQg4#S@~N!coe>C?=8N zanZ6>l~=3*W(%6s4X0qsE91h7N_mlVmOt^d*t`o4nj<+py;`(SMY&Pg#ev!7G$b6? zK5Y!Tm0nJ56&m5`vViZvN<4us(QnFX&k|XX(^e9Lp{{}al_&c|WthvkMxV*F1_8hJ z8cYx6x)#M;xpDgY%waIdHv>1iknq4LA@D5~ySB9J7HrhmV?`bKV=cs8irHb!r~9Ru zVjj}r;EaZT9&}`kghE)E?J6Dsg|9Izmsr&yUYx_YM=~1ROn0k4*J*mgT zeI6z#qw{WB8O&p4T%$gJE&8uz|8?lUj{WaJ|9i+!LbUTGH=_SW_9Lo{uReb>`fq0c`_Yd-`uq(z((8gfS8inO=!PiD7wo9` z;I$gr7pbWuv;KA+1Xv-sgAH#5J@^RQ3P*9#%-}sZAftd8KcK|k2c#UlQ2}n0fSVNH zCJDG%0mySaS6}Lzg~bguzC2hy17e8;Dsm0=1X>=DN~15J5>h@{1RXi`qFZu7s1%eP z4kid(f0FG^Rj{)qbGRzUOil!-+UT?T_EaV1uW@lkph}ACWHtASWUZ3p7^E|-xc2ci z4Ck@1UX2CnWYjgTkFgd31T23_FU^!opsFHk%;=>$M7$Mo(@Qm^5`yb}BEU(t=7``N zjvFY8VsHujGv$(7#`=y?^#yOjR?SQvD1B~yTKY2eK>7yiMd_>62c)lFcU9gQTLh>{ zfp`XHAjU#;$i=!q3TuP3mP3$h)s7T23^LrvTE9fqg>rz?t+@uo;BXn-w7Y2!ZhGa2 zq)IQlrP_7_W`l}R0P+L%*NWgR*yJ8&l)+6D9rr74eE3v9_G)pqru%b1IvjC%tKSOF zrkN@ZZ=h8}9Nvh+I1z?5v71YTLH~5~MgnjS0!(Z4v!I0SK0uCJ$P}`E&Ik1;Z&pgy z=w2`;_9&3!ma>^rDO)P!N_*uE_>)SaK%iJD=RhSx*Agg_&27CaNw_7J6Asc&7WG&j33I1f{wkEtzz(VmrdY`@)7q82+J zC0Y3t7?baf$7E3sNr=8i4d`nU1G)e`GYCCHeNUOMjWZWyW|?+KrM*t2y)I5WB-5@{ za!RH}QM+ky(`@-^>E8*5zzgtiwkkC@o;LJzSuD%^G2Z{E@FvgEa$ru{GN!Z+6WyRG z`?4AB^XV`k2S~7%^(!~>4Axy`m++0`t`fO8j*gadx0t_$#?irs)a?zqAVS@@GT@iV zL{J`mn10bjP;NaB^OU#*^960Cov~8RcJPA~p4EP~Fx30mx=`=041vy#ImrE{zaU~=8L|F>1Qs1d%lv@GN$ZC{Za=7<%GLYfH4aha6Ea`Vb`#w}ZMD0Wx zw0}bOk3#qx%(PYt7%Ti0YKAb|uQQ^xs1zDASz(2L#rc%@hgc*D#jsvGR7s1OeN+U7 zD-%i4TdGZ$RIwoNR8cVR$F*9)O}Kspo$~@zZXFf`6&nB-M%oAQM`<01N-MZI(ldm% z3fy6AYbakEmBBO9lJ;v&~662cDCY3@0zfU&Ayv8;?9RO+LQXQ z7AH z4!s6@<#@PuTSVX`YAP{^jqAi`vw%~S>%@8vVuWPYdGXLGyPWGp*`!E4dZ zdLj566BGHm0JK~l(NZV7*kS@&{v6fpX9ltclJD)Pj5w?BLwgpJ?~aIkFUN4T)^fUt z=X4Rz=^~!fMLegAc+QIu@63pJ&dVW}q!lbW666A%IiS?$*#`p4`0Ry(?FH=qqdj#@qNBGzdv*r4A&IZaf=+1FQXyT-p zzk7!n^Jc#(b6S>XQBEt|iZOPwwVtioYiSREjobTOF?ueP39E=)X`oaq?cTl`i@gRl zk|8nrN35@uA1?K*;D~FHV+zPC*#7eEdhjipF_S8(q|zGZ44rx!bA0S2atuW|J|=VA z{SrAwq8xY29Cs-WCeI%gOSG?3EY}VB*doZ?(bQJ%fi*AM{ejT9f1KcbX1MXnX;yF~ z?e*brrZ97WT^gD`$qJ4pE_P6hbvyT<+DV|CNHgf;5eU|Q(ANa~1TyC?mOWR%ojG^h z!ks+V3PEdfr>|df&(+l~e3Rl^wgrsFeCsnK6oO;WDGjkJEO?c_&QH8Znor0yAAgZF6!(zu=VH1?fqRmHPsH86hZL>B&onxMCT(`G4gncl>3mA6 zDdgTRf}cYwwJ!Ur6|X5UK71c4h!&6s${|QK&F+du`O6Ye!$_EWUhffHp4Fhl0O?nB;1Y+M(~sEtOhF}6@a@7UW7`-i%@oIVTU2-X zGMpu6?bsAc)Y8MRAkF?mlV<@R0Q`5RbrN>-B&@Qb-51)5&5WuEHIYe4EnnUak*)XH z(eg!NV>Lz^8P%T|SijO*w9(AiOE8cg`~{a(Hci}FF2eD{n`j#1@^Y}WqQ30c^&o|3 z5BhbBJVw_xll;HKG)!JimDx{kf-Y}N*F$JnMS z>2)e@{S((UGl~Ea1(QSqO~EG3MiLl^p5vG_ZnJaFbd!=u0^%?)1}`8g!GED85m1x9 z(&*YXa(tbt)?*`_G%+5xCIf}W*5cYm5;D}-TI%Zt4FhNhKpYxj#x6fChF(_Ls#wk? z?Ue^hJI|ooJvb(&mKd5-%e(4ug({zlw9quhm@r(IRbf4+N9*sGBNXD%E$i2t!07*C zcc;)BnD5^ssFH(6M0F)wG_!Ut^*9RaHEdgS!}50Ib2k%F|lspHzZ>QvEgobR4H~{|#*2xGv_O22a~a(OIf* zkYEfCGNIG0(84A8(-McLRaxZulFrNDGzbj@R0=`_6Dbs&A4H`gW5}$K;c<;t)>Oj4 zb{+I7Q+tc*Q?hDw9>bJ@jt5-7qtVINXVIZkH=Q47(XrScYstO#?08~-uk$UcO q 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