using System; using System.Buffers; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using ICSharpCode.SharpZipLib.BZip2; using ICSharpCode.SharpZipLib.Checksum; using ICSharpCode.SharpZipLib.Core; using ICSharpCode.SharpZipLib.Encryption; using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.Zip.Compression; using ICSharpCode.SharpZipLib.Zip.Compression.Streams; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("ICSharpCode.SharpZipLib.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b9a14ea8fc9d7599e0e82a1292a23103f0210e2f928a0f466963af23fffadba59dcc8c9e26ecd114d7c0b4179e4bc93b1656b7ee2d4a67dd7c1992653e0d9cc534f7914b6f583b022e0a7aa8a430f407932f9a6806f0fc64d61e78d5ae01aa8f8233196719d44da2c50a2d1cfa3f7abb7487b3567a4f0456aa6667154c6749b1")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")] [assembly: AssemblyMetadata("IsTrimmable", "True")] [assembly: AssemblyCompany("ICSharpCode")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © 2000-2022 SharpZipLib Contributors")] [assembly: AssemblyDescription("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)")] [assembly: AssemblyFileVersion("1.4.0.12")] [assembly: AssemblyInformationalVersion("1.4.0+cea8b0dc154c71040ff74d731ff55c451e8dc59d")] [assembly: AssemblyProduct("ICSharpCode.SharpZipLib")] [assembly: AssemblyTitle("ICSharpCode.SharpZipLib")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/icsharpcode/SharpZipLib")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: AssemblyVersion("1.4.0.12")] namespace ICSharpCode.SharpZipLib { [Serializable] public class SharpZipBaseException : Exception { public SharpZipBaseException() { } public SharpZipBaseException(string message) : base(message) { } public SharpZipBaseException(string message, Exception innerException) : base(message, innerException) { } protected SharpZipBaseException(SerializationInfo info, StreamingContext context) : base(info, context) { } } [Serializable] public class StreamDecodingException : SharpZipBaseException { private const string GenericMessage = "Input stream could not be decoded"; public StreamDecodingException() : base("Input stream could not be decoded") { } public StreamDecodingException(string message) : base(message) { } public StreamDecodingException(string message, Exception innerException) : base(message, innerException) { } protected StreamDecodingException(SerializationInfo info, StreamingContext context) : base(info, context) { } } [Serializable] public class StreamUnsupportedException : StreamDecodingException { private const string GenericMessage = "Input stream is in a unsupported format"; public StreamUnsupportedException() : base("Input stream is in a unsupported format") { } public StreamUnsupportedException(string message) : base(message) { } public StreamUnsupportedException(string message, Exception innerException) : base(message, innerException) { } protected StreamUnsupportedException(SerializationInfo info, StreamingContext context) : base(info, context) { } } [Serializable] public class UnexpectedEndOfStreamException : StreamDecodingException { private const string GenericMessage = "Input stream ended unexpectedly"; public UnexpectedEndOfStreamException() : base("Input stream ended unexpectedly") { } public UnexpectedEndOfStreamException(string message) : base(message) { } public UnexpectedEndOfStreamException(string message, Exception innerException) : base(message, innerException) { } protected UnexpectedEndOfStreamException(SerializationInfo info, StreamingContext context) : base(info, context) { } } [Serializable] public class ValueOutOfRangeException : StreamDecodingException { public ValueOutOfRangeException(string nameOfValue) : base(nameOfValue + " out of range") { } public ValueOutOfRangeException(string nameOfValue, long value, long maxValue, long minValue = 0L) : this(nameOfValue, value.ToString(), maxValue.ToString(), minValue.ToString()) { } 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) { } protected ValueOutOfRangeException(SerializationInfo info, StreamingContext context) : base(info, context) { } } } namespace ICSharpCode.SharpZipLib.Zip { public class FastZipEvents { public ProcessFileHandler ProcessFile; public ProgressHandler Progress; public CompletedFileHandler CompletedFile; public DirectoryFailureHandler DirectoryFailure; public FileFailureHandler FileFailure; private TimeSpan progressInterval_ = TimeSpan.FromSeconds(3.0); public TimeSpan ProgressInterval { get { return progressInterval_; } set { progressInterval_ = value; } } public event EventHandler ProcessDirectory; public bool OnDirectoryFailure(string directory, Exception e) { bool result = false; DirectoryFailureHandler directoryFailure = DirectoryFailure; if (directoryFailure != null) { ScanFailureEventArgs e2 = new ScanFailureEventArgs(directory, e); directoryFailure(this, e2); result = e2.ContinueRunning; } return result; } public bool OnFileFailure(string file, Exception e) { FileFailureHandler fileFailure = FileFailure; bool flag = fileFailure != null; if (flag) { ScanFailureEventArgs e2 = new ScanFailureEventArgs(file, e); fileFailure(this, e2); flag = e2.ContinueRunning; } return flag; } public bool OnProcessFile(string file) { bool result = true; ProcessFileHandler processFile = ProcessFile; if (processFile != null) { ScanEventArgs e = new ScanEventArgs(file); processFile(this, e); result = e.ContinueRunning; } return result; } public bool OnCompletedFile(string file) { bool result = true; CompletedFileHandler completedFile = CompletedFile; if (completedFile != null) { ScanEventArgs e = new ScanEventArgs(file); completedFile(this, e); result = e.ContinueRunning; } return result; } public bool OnProcessDirectory(string directory, bool hasMatchingFiles) { bool result = true; EventHandler eventHandler = this.ProcessDirectory; if (eventHandler != null) { DirectoryEventArgs e = new DirectoryEventArgs(directory, hasMatchingFiles); eventHandler(this, e); result = e.ContinueRunning; } return result; } } public class FastZip { public enum Overwrite { Prompt, Never, Always } public delegate bool ConfirmOverwriteDelegate(string fileName); 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 Deflater.CompressionLevel compressionLevel_ = Deflater.CompressionLevel.DEFAULT_COMPRESSION; private StringCodec _stringCodec = new StringCodec(); private string password_; public bool CreateEmptyDirectories { get { return createEmptyDirectories_; } set { createEmptyDirectories_ = value; } } public string Password { get { return password_; } set { password_ = value; } } public ZipEncryptionMethod EntryEncryptionMethod { get; set; } = ZipEncryptionMethod.ZipCrypto; public INameTransform NameTransform { get { return entryFactory_.NameTransform; } set { entryFactory_.NameTransform = value; } } public IEntryFactory EntryFactory { get { return entryFactory_; } set { if (value == null) { entryFactory_ = new ZipEntryFactory(); } else { entryFactory_ = value; } } } public UseZip64 UseZip64 { get { return useZip64_; } set { useZip64_ = value; } } public bool RestoreDateTimeOnExtract { get { return restoreDateTimeOnExtract_; } set { restoreDateTimeOnExtract_ = value; } } public bool RestoreAttributesOnExtract { get { return restoreAttributesOnExtract_; } set { restoreAttributesOnExtract_ = value; } } public Deflater.CompressionLevel CompressionLevel { get { return compressionLevel_; } set { compressionLevel_ = value; } } public bool UseUnicode { get { return !_stringCodec.ForceZipLegacyEncoding; } set { _stringCodec.ForceZipLegacyEncoding = !value; } } public int LegacyCodePage { get { return _stringCodec.CodePage; } set { _stringCodec.CodePage = value; } } public StringCodec StringCodec { get { return _stringCodec; } set { _stringCodec = value; } } public FastZip() { } public FastZip(ZipEntryFactory.TimeSetting timeSetting) { entryFactory_ = new ZipEntryFactory(timeSetting); restoreDateTimeOnExtract_ = true; } public FastZip(DateTime time) { entryFactory_ = new ZipEntryFactory(time); restoreDateTimeOnExtract_ = true; } public FastZip(FastZipEvents events) { events_ = events; } public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter) { CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, directoryFilter); } public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter) { CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, null); } public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter) { CreateZip(outputStream, sourceDirectory, recurse, fileFilter, directoryFilter, leaveOpen: false); } public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter, bool leaveOpen) { FileSystemScanner scanner = new FileSystemScanner(fileFilter, directoryFilter); CreateZip(outputStream, sourceDirectory, recurse, scanner, leaveOpen); } public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, IScanFilter fileFilter, IScanFilter directoryFilter) { CreateZip(File.Create(zipFileName), sourceDirectory, recurse, fileFilter, directoryFilter); } public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse, IScanFilter fileFilter, IScanFilter directoryFilter, bool leaveOpen = false) { FileSystemScanner scanner = new FileSystemScanner(fileFilter, directoryFilter); CreateZip(outputStream, sourceDirectory, recurse, scanner, leaveOpen); } 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; if (!string.IsNullOrEmpty(password_) && EntryEncryptionMethod != ZipEncryptionMethod.None) { outputStream_.Password = password_; } outputStream_.UseZip64 = UseZip64; scanner.ProcessFile = (ProcessFileHandler)Delegate.Combine(scanner.ProcessFile, new ProcessFileHandler(ProcessFile)); if (CreateEmptyDirectories) { scanner.ProcessDirectory += ProcessDirectory; } if (events_ != null) { if (events_.FileFailure != null) { scanner.FileFailure = (FileFailureHandler)Delegate.Combine(scanner.FileFailure, events_.FileFailure); } if (events_.DirectoryFailure != null) { scanner.DirectoryFailure = (DirectoryFailureHandler)Delegate.Combine(scanner.DirectoryFailure, events_.DirectoryFailure); } } scanner.Scan(sourceDirectory, recurse); } } public void ExtractZip(string zipFileName, string targetDirectory, string fileFilter) { ExtractZip(zipFileName, targetDirectory, Overwrite.Always, null, fileFilter, null, restoreDateTimeOnExtract_); } 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, isStreamOwner: true, allowParentTraversal); } 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("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_; } IEnumerator enumerator = zipFile_.GetEnumerator(); while (continueRunning_ && enumerator.MoveNext()) { ZipEntry zipEntry = (ZipEntry)enumerator.Current; if (zipEntry.IsFile) { if (directoryFilter_.IsMatch(Path.GetDirectoryName(zipEntry.Name)) && fileFilter_.IsMatch(zipEntry.Name)) { ExtractEntry(zipEntry); } } else if (zipEntry.IsDirectory && directoryFilter_.IsMatch(zipEntry.Name) && CreateEmptyDirectories) { ExtractEntry(zipEntry); } } } } private void ProcessDirectory(object sender, DirectoryEventArgs e) { if (!e.HasMatchingFiles && CreateEmptyDirectories) { if (events_ != null) { events_.OnProcessDirectory(e.Name, e.HasMatchingFiles); } if (e.ContinueRunning && 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) { return; } try { using FileStream stream = File.Open(e.Name, FileMode.Open, FileAccess.Read, FileShare.Read); ZipEntry zipEntry = entryFactory_.MakeFileEntry(e.Name); if (_stringCodec.ForceZipLegacyEncoding) { zipEntry.IsUnicodeText = false; } ConfigureEntryEncryption(zipEntry); outputStream_.PutNextEntry(zipEntry); AddFileContents(e.Name, stream); } catch (Exception e2) { if (events_ != null) { continueRunning_ = events_.OnFileFailure(e.Name, e2); return; } continueRunning_ = false; throw; } } private void ConfigureEntryEncryption(ZipEntry entry) { 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("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 flag = true; if (overwrite_ != Overwrite.Always && File.Exists(targetName)) { flag = overwrite_ == Overwrite.Prompt && confirmDelegate_ != null && confirmDelegate_(targetName); } if (!flag) { return; } if (events_ != null) { continueRunning_ = events_.OnProcessFile(entry.Name); } if (!continueRunning_) { return; } try { using (FileStream destination = File.Create(targetName)) { if (buffer_ == null) { buffer_ = new byte[4096]; } using (Stream source = zipFile_.GetInputStream(entry)) { if (events_ != null && events_.Progress != null) { StreamUtils.Copy(source, destination, buffer_, events_.Progress, events_.ProgressInterval, this, entry.Name, entry.Size); } else { StreamUtils.Copy(source, destination, buffer_); } } if (events_ != null) { continueRunning_ = events_.OnCompletedFile(entry.Name); } } if (restoreDateTimeOnExtract_) { switch (entryFactory_.Setting) { case ZipEntryFactory.TimeSetting.CreateTime: File.SetCreationTime(targetName, entry.DateTime); break; case ZipEntryFactory.TimeSetting.CreateTimeUtc: File.SetCreationTimeUtc(targetName, entry.DateTime); break; case ZipEntryFactory.TimeSetting.LastAccessTime: File.SetLastAccessTime(targetName, entry.DateTime); break; case ZipEntryFactory.TimeSetting.LastAccessTimeUtc: File.SetLastAccessTimeUtc(targetName, entry.DateTime); break; case ZipEntryFactory.TimeSetting.LastWriteTime: File.SetLastWriteTime(targetName, entry.DateTime); break; case ZipEntryFactory.TimeSetting.LastWriteTimeUtc: File.SetLastWriteTimeUtc(targetName, entry.DateTime); break; case ZipEntryFactory.TimeSetting.Fixed: File.SetLastWriteTime(targetName, entryFactory_.FixedDateTime); break; default: throw new ZipException("Unhandled time setting in ExtractFileEntry"); } } if (RestoreAttributesOnExtract && entry.IsDOSEntry && entry.ExternalFileAttributes != -1) { FileAttributes externalFileAttributes = (FileAttributes)entry.ExternalFileAttributes; externalFileAttributes &= FileAttributes.ReadOnly | FileAttributes.Hidden | FileAttributes.Archive | FileAttributes.Normal; File.SetAttributes(targetName, externalFileAttributes); } } catch (Exception e) { if (events_ != null) { continueRunning_ = events_.OnFileFailure(targetName, e); return; } continueRunning_ = false; throw; } } private void ExtractEntry(ZipEntry entry) { bool flag = entry.IsCompressionMethodSupported(); string text = entry.Name; if (flag) { if (entry.IsFile) { text = extractNameTransform_.TransformFile(text); } else if (entry.IsDirectory) { text = extractNameTransform_.TransformDirectory(text); } flag = !string.IsNullOrEmpty(text); } string text2 = string.Empty; if (flag) { text2 = ((!entry.IsDirectory) ? Path.GetDirectoryName(Path.GetFullPath(text)) : text); } if (flag && !Directory.Exists(text2) && (!entry.IsDirectory || CreateEmptyDirectories)) { try { continueRunning_ = events_?.OnProcessDirectory(text2, hasMatchingFiles: true) ?? true; if (continueRunning_) { Directory.CreateDirectory(text2); if (entry.IsDirectory && restoreDateTimeOnExtract_) { switch (entryFactory_.Setting) { case ZipEntryFactory.TimeSetting.CreateTime: Directory.SetCreationTime(text2, entry.DateTime); break; case ZipEntryFactory.TimeSetting.CreateTimeUtc: Directory.SetCreationTimeUtc(text2, entry.DateTime); break; case ZipEntryFactory.TimeSetting.LastAccessTime: Directory.SetLastAccessTime(text2, entry.DateTime); break; case ZipEntryFactory.TimeSetting.LastAccessTimeUtc: Directory.SetLastAccessTimeUtc(text2, entry.DateTime); break; case ZipEntryFactory.TimeSetting.LastWriteTime: Directory.SetLastWriteTime(text2, entry.DateTime); break; case ZipEntryFactory.TimeSetting.LastWriteTimeUtc: Directory.SetLastWriteTimeUtc(text2, entry.DateTime); break; case ZipEntryFactory.TimeSetting.Fixed: Directory.SetLastWriteTime(text2, entryFactory_.FixedDateTime); break; default: throw new ZipException("Unhandled time setting in ExtractEntry"); } } } else { flag = false; } } catch (Exception e) { flag = false; if (events_ == null) { continueRunning_ = false; throw; } if (entry.IsDirectory) { continueRunning_ = events_.OnDirectoryFailure(text, e); } else { continueRunning_ = events_.OnFileFailure(text, e); } } } if (flag && entry.IsFile) { ExtractFileEntry(entry, text); } } private static int MakeExternalAttributes(FileInfo info) { return (int)info.Attributes; } private static bool NameIsValid(string name) { if (!string.IsNullOrEmpty(name)) { return name.IndexOfAny(Path.GetInvalidPathChars()) < 0; } return false; } } public interface IEntryFactory { INameTransform NameTransform { get; set; } ZipEntryFactory.TimeSetting Setting { get; } DateTime FixedDateTime { get; } ZipEntry MakeFileEntry(string fileName); ZipEntry MakeFileEntry(string fileName, bool useFileSystem); ZipEntry MakeFileEntry(string fileName, string entryName, bool useFileSystem); ZipEntry MakeDirectoryEntry(string directoryName); ZipEntry MakeDirectoryEntry(string directoryName, bool useFileSystem); } public class WindowsNameTransform : INameTransform { private const int MaxPath = 260; private string _baseDirectory; private bool _trimIncomingPaths; private char _replacementChar = '_'; private bool _allowParentTraversal; private static readonly char[] InvalidEntryChars = new char[39] { '"', '<', '>', '|', '\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', '*', '?', ':' }; public string BaseDirectory { get { return _baseDirectory; } set { if (value == null) { throw new ArgumentNullException("value"); } _baseDirectory = Path.GetFullPath(value); } } public bool AllowParentTraversal { get { return _allowParentTraversal; } set { _allowParentTraversal = value; } } public bool TrimIncomingPaths { get { return _trimIncomingPaths; } set { _trimIncomingPaths = value; } } 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; } } public WindowsNameTransform(string baseDirectory, bool allowParentTraversal = false) { BaseDirectory = baseDirectory ?? throw new ArgumentNullException("baseDirectory", "Directory name is invalid"); AllowParentTraversal = allowParentTraversal; } public WindowsNameTransform() { } public string TransformDirectory(string name) { name = TransformFile(name); if (name.Length > 0) { while (true) { string text = name; char directorySeparatorChar = Path.DirectorySeparatorChar; if (text.EndsWith(directorySeparatorChar.ToString(), StringComparison.Ordinal)) { name = name.Remove(name.Length - 1, 1); continue; } break; } return name; } throw new InvalidNameException("Cannot have an empty directory name"); } public string TransformFile(string name) { if (name != null) { name = MakeValidName(name, _replacementChar); if (_trimIncomingPaths) { name = Path.GetFileName(name); } if (_baseDirectory != null) { name = Path.Combine(_baseDirectory, name); string text = Path.GetFullPath(_baseDirectory); if (text[text.Length - 1] != Path.DirectorySeparatorChar) { string text2 = text; char directorySeparatorChar = Path.DirectorySeparatorChar; text = text2 + directorySeparatorChar; } if (!_allowParentTraversal && !Path.GetFullPath(name).StartsWith(text, StringComparison.InvariantCultureIgnoreCase)) { throw new InvalidNameException("Parent traversal in paths is not allowed"); } } } else { name = string.Empty; } return name; } public static bool IsValidName(string name) { if (name != null && name.Length <= 260) { return string.Compare(name, MakeValidName(name, '_'), StringComparison.Ordinal) == 0; } return false; } public static string MakeValidName(string name, char replacement) { if (name == null) { throw new ArgumentNullException("name"); } string text = name; char directorySeparatorChar = Path.DirectorySeparatorChar; name = PathUtils.DropPathRoot(text.Replace("/", directorySeparatorChar.ToString())); while (name.Length > 0 && name[0] == Path.DirectorySeparatorChar) { name = name.Remove(0, 1); } while (name.Length > 0 && name[name.Length - 1] == Path.DirectorySeparatorChar) { name = name.Remove(name.Length - 1, 1); } int num; for (num = name.IndexOf(string.Format("{0}{0}", Path.DirectorySeparatorChar), StringComparison.Ordinal); num >= 0; num = name.IndexOf(string.Format("{0}{0}", Path.DirectorySeparatorChar), StringComparison.Ordinal)) { name = name.Remove(num, 1); } num = name.IndexOfAny(InvalidEntryChars); if (num >= 0) { StringBuilder stringBuilder = new StringBuilder(name); while (num >= 0) { stringBuilder[num] = replacement; num = ((num < name.Length) ? name.IndexOfAny(InvalidEntryChars, num + 1) : (-1)); } name = stringBuilder.ToString(); } if (name.Length > 260) { throw new PathTooLongException(); } return name; } } public enum UseZip64 { Off, On, Dynamic } public enum CompressionMethod { Stored = 0, Deflated = 8, Deflate64 = 9, BZip2 = 12, LZMA = 14, PPMd = 98, WinZipAES = 99 } public enum EncryptionAlgorithm { None = 0, PkzipClassic = 1, Des = 26113, RC2 = 26114, TripleDes168 = 26115, TripleDes112 = 26121, Aes128 = 26126, Aes192 = 26127, Aes256 = 26128, RC2Corrected = 26370, Blowfish = 26400, Twofish = 26401, RC4 = 26625, Unknown = 65535 } [Flags] public enum GeneralBitFlags { Encrypted = 1, Method = 6, Descriptor = 8, ReservedPKware4 = 0x10, Patched = 0x20, StrongEncryption = 0x40, Unused7 = 0x80, Unused8 = 0x100, Unused9 = 0x200, Unused10 = 0x400, UnicodeText = 0x800, EnhancedCompress = 0x1000, HeaderMasked = 0x2000, ReservedPkware14 = 0x4000, ReservedPkware15 = 0x8000 } public static class GeneralBitFlagsExtensions { public static bool Includes(this GeneralBitFlags flagData, GeneralBitFlags flag) { return (flag & flagData) != 0; } } public static class ZipConstants { public const int VersionMadeBy = 51; [Obsolete("Use VersionMadeBy instead")] public const int VERSION_MADE_BY = 51; public const int VersionStrongEncryption = 50; [Obsolete("Use VersionStrongEncryption instead")] public const int VERSION_STRONG_ENCRYPTION = 50; public const int VERSION_AES = 51; public const int VersionZip64 = 45; public const int VersionBZip2 = 46; public const int LocalHeaderBaseSize = 30; [Obsolete("Use LocalHeaderBaseSize instead")] public const int LOCHDR = 30; public const int Zip64DataDescriptorSize = 24; public const int DataDescriptorSize = 16; [Obsolete("Use DataDescriptorSize instead")] public const int EXTHDR = 16; public const int CentralHeaderBaseSize = 46; [Obsolete("Use CentralHeaderBaseSize instead")] public const int CENHDR = 46; public const int EndOfCentralRecordBaseSize = 22; [Obsolete("Use EndOfCentralRecordBaseSize instead")] public const int ENDHDR = 22; public const int CryptoHeaderSize = 12; [Obsolete("Use CryptoHeaderSize instead")] public const int CRYPTO_HEADER_SIZE = 12; public const int Zip64EndOfCentralDirectoryLocatorSize = 20; public const int LocalHeaderSignature = 67324752; [Obsolete("Use LocalHeaderSignature instead")] public const int LOCSIG = 67324752; public const int SpanningSignature = 134695760; [Obsolete("Use SpanningSignature instead")] public const int SPANNINGSIG = 134695760; public const int SpanningTempSignature = 808471376; [Obsolete("Use SpanningTempSignature instead")] public const int SPANTEMPSIG = 808471376; public const int DataDescriptorSignature = 134695760; [Obsolete("Use DataDescriptorSignature instead")] public const int EXTSIG = 134695760; [Obsolete("Use CentralHeaderSignature instead")] public const int CENSIG = 33639248; public const int CentralHeaderSignature = 33639248; public const int Zip64CentralFileHeaderSignature = 101075792; [Obsolete("Use Zip64CentralFileHeaderSignature instead")] public const int CENSIG64 = 101075792; public const int Zip64CentralDirLocatorSignature = 117853008; public const int ArchiveExtraDataSignature = 117853008; public const int CentralHeaderDigitalSignature = 84233040; [Obsolete("Use CentralHeaderDigitalSignaure instead")] public const int CENDIGITALSIG = 84233040; public const int EndOfCentralDirectorySignature = 101010256; [Obsolete("Use EndOfCentralDirectorySignature instead")] public const int ENDSIG = 101010256; } public static class GenericBitFlagsExtensions { public static bool HasAny(this GeneralBitFlags target, GeneralBitFlags flags) { return (target & flags) != 0; } public static bool HasAll(this GeneralBitFlags target, GeneralBitFlags flags) { return (target & flags) == flags; } } public enum ZipEncryptionMethod { None, ZipCrypto, AES128, AES256 } public enum HostSystemID { Msdos = 0, Amiga = 1, OpenVms = 2, Unix = 3, VMCms = 4, AtariST = 5, OS2 = 6, Macintosh = 7, ZSystem = 8, Cpm = 9, WindowsNT = 10, MVS = 11, Vse = 12, AcornRisc = 13, Vfat = 14, AlternateMvs = 15, BeOS = 16, Tandem = 17, OS400 = 18, OSX = 19, WinZipAES = 99 } public class ZipEntry { [Flags] private enum Known : byte { None = 0, Size = 1, CompressedSize = 2, Crc = 4, Time = 8, ExternalAttributes = 0x10 } private Known known; private int externalFileAttributes = -1; private ushort versionMadeBy; private string name; private ulong size; private ulong compressedSize; private ushort versionToExtract; private uint crc; private DateTime dateTime; private CompressionMethod method = CompressionMethod.Deflated; private byte[] extra; private string comment; private int flags; private long zipFileIndex = -1L; private long offset; private bool forceZip64_; private byte cryptoCheckValue_; private int _aesVer; private int _aesEncryptionStrength; public bool HasCrc => (known & Known.Crc) != 0; public bool IsCrypted { get { return this.HasFlag(GeneralBitFlags.Encrypted); } set { this.SetFlag(GeneralBitFlags.Encrypted, value); } } public bool IsUnicodeText { get { return this.HasFlag(GeneralBitFlags.UnicodeText); } set { this.SetFlag(GeneralBitFlags.UnicodeText, value); } } internal byte CryptoCheckValue { get { return cryptoCheckValue_; } set { cryptoCheckValue_ = value; } } public int Flags { get { return flags; } set { flags = value; } } public long ZipFileIndex { get { return zipFileIndex; } set { zipFileIndex = value; } } public long Offset { get { return offset; } set { offset = value; } } public int ExternalFileAttributes { get { if ((known & Known.ExternalAttributes) != Known.None) { return externalFileAttributes; } return -1; } set { externalFileAttributes = value; known |= Known.ExternalAttributes; } } public int VersionMadeBy => versionMadeBy & 0xFF; public bool IsDOSEntry { get { if (HostSystem != 0) { return HostSystem == 10; } return true; } } public int HostSystem { get { return (versionMadeBy >> 8) & 0xFF; } set { versionMadeBy &= 255; versionMadeBy |= (ushort)((value & 0xFF) << 8); } } public int Version { get { if (versionToExtract != 0) { return versionToExtract & 0xFF; } if (AESKeySize > 0) { return 51; } if (CompressionMethod.BZip2 == method) { return 46; } if (CentralHeaderRequiresZip64) { return 45; } if (CompressionMethod.Deflated == method || IsDirectory || IsCrypted) { return 20; } if (HasDosAttributes(8)) { return 11; } return 10; } } public bool CanDecompress { get { if (Version <= 51 && (Version == 10 || Version == 11 || Version == 20 || Version == 45 || Version == 46 || Version == 51)) { return IsCompressionMethodSupported(); } return false; } } public bool LocalHeaderRequiresZip64 { get { bool flag = forceZip64_; if (!flag) { ulong num = compressedSize; if (versionToExtract == 0 && IsCrypted) { num += (ulong)EncryptionOverheadSize; } flag = (size >= uint.MaxValue || num >= uint.MaxValue) && (versionToExtract == 0 || versionToExtract >= 45); } return flag; } } public bool CentralHeaderRequiresZip64 { get { if (!LocalHeaderRequiresZip64) { return offset >= uint.MaxValue; } return true; } } public long DosTime { get { if ((known & Known.Time) == 0) { return 0L; } uint num = (uint)DateTime.Year; uint num2 = (uint)DateTime.Month; uint num3 = (uint)DateTime.Day; uint num4 = (uint)DateTime.Hour; uint num5 = (uint)DateTime.Minute; uint num6 = (uint)DateTime.Second; if (num < 1980) { num = 1980u; num2 = 1u; num3 = 1u; num4 = 0u; num5 = 0u; num6 = 0u; } else if (num > 2107) { num = 2107u; num2 = 12u; num3 = 31u; num4 = 23u; num5 = 59u; num6 = 59u; } return (((num - 1980) & 0x7F) << 25) | (num2 << 21) | (num3 << 16) | (num4 << 11) | (num5 << 5) | (num6 >> 1); } set { uint num = (uint)value; uint second = Math.Min(59u, 2 * (num & 0x1F)); uint minute = Math.Min(59u, (num >> 5) & 0x3F); uint hour = Math.Min(23u, (num >> 11) & 0x1F); uint month = Math.Max(1u, Math.Min(12u, (uint)((int)(value >> 21) & 0xF))); uint year = ((num >> 25) & 0x7F) + 1980; int day = Math.Max(1, Math.Min(DateTime.DaysInMonth((int)year, (int)month), (int)((value >> 16) & 0x1F))); DateTime = new DateTime((int)year, (int)month, day, (int)hour, (int)minute, (int)second, DateTimeKind.Unspecified); } } public DateTime DateTime { get { return dateTime; } set { dateTime = value; known |= Known.Time; } } public string Name { get { return name; } internal set { name = value; } } public long Size { get { if ((known & Known.Size) == 0) { return -1L; } return (long)size; } set { size = (ulong)value; known |= Known.Size; } } public long CompressedSize { get { if ((known & Known.CompressedSize) == 0) { return -1L; } return (long)compressedSize; } set { compressedSize = (ulong)value; known |= Known.CompressedSize; } } public long Crc { get { if ((known & Known.Crc) == 0) { return -1L; } return (long)crc & 0xFFFFFFFFL; } set { if ((crc & -4294967296L) != 0L) { throw new ArgumentOutOfRangeException("value"); } crc = (uint)value; known |= Known.Crc; } } public CompressionMethod CompressionMethod { get { return method; } set { method = value; } } internal CompressionMethod CompressionMethodForHeader { get { if (AESKeySize <= 0) { return method; } return CompressionMethod.WinZipAES; } } public byte[] ExtraData { get { return extra; } set { if (value == null) { extra = null; return; } if (value.Length > 65535) { throw new ArgumentOutOfRangeException("value"); } extra = new byte[value.Length]; Array.Copy(value, 0, extra, 0, value.Length); } } public int AESKeySize { get { return _aesEncryptionStrength switch { 0 => 0, 1 => 128, 2 => 192, 3 => 256, _ => 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); } } } internal byte AESEncryptionStrength => (byte)_aesEncryptionStrength; internal int AESSaltLen => AESKeySize / 16; internal int AESOverheadSize => 12 + AESSaltLen; internal int EncryptionOverheadSize { get { if (IsCrypted) { if (_aesEncryptionStrength != 0) { return AESOverheadSize; } return 12; } return 0; } } public string Comment { get { return comment; } set { if (value != null && value.Length > 65535) { throw new ArgumentOutOfRangeException("value", "cannot exceed 65535"); } comment = value; } } public bool IsDirectory { get { if (name.Length <= 0 || (name[name.Length - 1] != '/' && name[name.Length - 1] != '\\')) { return HasDosAttributes(16); } return true; } } public bool IsFile { get { if (!IsDirectory) { return !HasDosAttributes(8); } return false; } } public ZipEntry(string name) : this(name, 0, 51, CompressionMethod.Deflated, unicode: true) { } internal ZipEntry(string name, int versionRequiredToExtract) : this(name, versionRequiredToExtract, 51, CompressionMethod.Deflated, unicode: true) { } internal ZipEntry(string name, int versionRequiredToExtract, int madeByInfo, CompressionMethod method, bool unicode) { if (name == null) { throw new ArgumentNullException("name"); } if (name.Length > 65535) { throw new ArgumentException("Name is too long", "name"); } if (versionRequiredToExtract != 0 && versionRequiredToExtract < 10) { throw new ArgumentOutOfRangeException("versionRequiredToExtract"); } DateTime = DateTime.Now; this.name = name; versionMadeBy = (ushort)madeByInfo; versionToExtract = (ushort)versionRequiredToExtract; this.method = method; IsUnicodeText = unicode; } [Obsolete("Use Clone instead")] public ZipEntry(ZipEntry entry) { if (entry == null) { throw new ArgumentNullException("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); } } private bool HasDosAttributes(int attributes) { bool flag = false; if ((known & Known.ExternalAttributes) != Known.None) { flag |= (HostSystem == 0 || HostSystem == 10) && (ExternalFileAttributes & attributes) == attributes; } return flag; } public void ForceZip64() { forceZip64_ = true; } public bool IsZip64Forced() { return forceZip64_; } internal void ProcessExtraData(bool localHeader) { ZipExtraData zipExtraData = new ZipExtraData(extra); if (zipExtraData.Find(1)) { forceZip64_ = true; if (zipExtraData.ValueLength < 4) { throw new ZipException("Extra data extended Zip64 information length is invalid"); } if (size == uint.MaxValue) { size = (ulong)zipExtraData.ReadLong(); } if (compressedSize == uint.MaxValue) { compressedSize = (ulong)zipExtraData.ReadLong(); } if (!localHeader && offset == uint.MaxValue) { offset = zipExtraData.ReadLong(); } } else if ((versionToExtract & 0xFF) >= 45 && (size == uint.MaxValue || compressedSize == uint.MaxValue)) { throw new ZipException("Zip64 Extended information required but is missing."); } DateTime = GetDateTime(zipExtraData) ?? DateTime; if (method == CompressionMethod.WinZipAES) { ProcessAESExtraData(zipExtraData); } } private static DateTime? GetDateTime(ZipExtraData extraData) { ExtendedUnixData data = extraData.GetData(); if (data != null && data.Include.HasFlag(ExtendedUnixData.Flags.ModificationTime)) { return data.ModificationTime; } return null; } private void ProcessAESExtraData(ZipExtraData extraData) { if (extraData.Find(39169)) { versionToExtract = 51; int valueLength = extraData.ValueLength; if (valueLength < 7) { throw new ZipException("AES Extra Data Length " + valueLength + " invalid."); } int aesVer = extraData.ReadShort(); extraData.ReadShort(); int aesEncryptionStrength = extraData.ReadByte(); int num = extraData.ReadShort(); _aesVer = aesVer; _aesEncryptionStrength = aesEncryptionStrength; method = (CompressionMethod)num; return; } throw new ZipException("AES Extra Data missing"); } public bool IsCompressionMethodSupported() { return IsCompressionMethodSupported(CompressionMethod); } public object Clone() { ZipEntry zipEntry = (ZipEntry)MemberwiseClone(); if (extra != null) { zipEntry.extra = new byte[extra.Length]; Array.Copy(extra, 0, zipEntry.extra, 0, extra.Length); } return zipEntry; } public override string ToString() { return name; } public static bool IsCompressionMethodSupported(CompressionMethod method) { if (method != CompressionMethod.Deflated && method != CompressionMethod.Stored) { return method == CompressionMethod.BZip2; } return true; } public static string CleanName(string name) { if (name == null) { return string.Empty; } if (Path.IsPathRooted(name)) { name = name.Substring(Path.GetPathRoot(name).Length); } name = name.Replace("\\", "/"); while (name.Length > 0 && name[0] == '/') { name = name.Remove(0, 1); } return name; } } public static class ZipEntryExtensions { public static bool HasFlag(this ZipEntry entry, GeneralBitFlags flag) { return ((uint)entry.Flags & (uint)flag) != 0; } public static void SetFlag(this ZipEntry entry, GeneralBitFlags flag, bool enabled = true) { entry.Flags = (enabled ? (entry.Flags | (int)flag) : (entry.Flags & (int)(~flag))); } } public class ZipEntryFactory : IEntryFactory { public enum TimeSetting { LastWriteTime, LastWriteTimeUtc, CreateTime, CreateTimeUtc, LastAccessTime, LastAccessTimeUtc, Fixed } private INameTransform nameTransform_; private DateTime fixedDateTime_ = DateTime.Now; private TimeSetting timeSetting_; private bool isUnicodeText_; private int getAttributes_ = -1; private int setAttributes_; public INameTransform NameTransform { get { return nameTransform_; } set { if (value == null) { nameTransform_ = new ZipNameTransform(); } else { nameTransform_ = value; } } } public TimeSetting Setting { get { return timeSetting_; } set { timeSetting_ = value; } } public DateTime FixedDateTime { get { return fixedDateTime_; } set { if (value.Year < 1970) { throw new ArgumentException("Value is too old to be valid", "value"); } fixedDateTime_ = value; } } public int GetAttributes { get { return getAttributes_; } set { getAttributes_ = value; } } public int SetAttributes { get { return setAttributes_; } set { setAttributes_ = value; } } public bool IsUnicodeText { get { return isUnicodeText_; } set { isUnicodeText_ = value; } } public ZipEntryFactory() { nameTransform_ = new ZipNameTransform(); isUnicodeText_ = true; } public ZipEntryFactory(TimeSetting timeSetting) : this() { timeSetting_ = timeSetting; } public ZipEntryFactory(DateTime time) : this() { timeSetting_ = TimeSetting.Fixed; FixedDateTime = time; } public ZipEntry MakeFileEntry(string fileName) { return MakeFileEntry(fileName, null, useFileSystem: true); } public ZipEntry MakeFileEntry(string fileName, bool useFileSystem) { return MakeFileEntry(fileName, null, useFileSystem); } public ZipEntry MakeFileEntry(string fileName, string entryName, bool useFileSystem) { ZipEntry zipEntry = new ZipEntry(nameTransform_.TransformFile((!string.IsNullOrEmpty(entryName)) ? entryName : fileName)); zipEntry.IsUnicodeText = isUnicodeText_; int num = 0; bool flag = setAttributes_ != 0; FileInfo fileInfo = null; if (useFileSystem) { fileInfo = new FileInfo(fileName); } if (fileInfo != null && fileInfo.Exists) { switch (timeSetting_) { case TimeSetting.CreateTime: zipEntry.DateTime = fileInfo.CreationTime; break; case TimeSetting.CreateTimeUtc: zipEntry.DateTime = fileInfo.CreationTimeUtc; break; case TimeSetting.LastAccessTime: zipEntry.DateTime = fileInfo.LastAccessTime; break; case TimeSetting.LastAccessTimeUtc: zipEntry.DateTime = fileInfo.LastAccessTimeUtc; break; case TimeSetting.LastWriteTime: zipEntry.DateTime = fileInfo.LastWriteTime; break; case TimeSetting.LastWriteTimeUtc: zipEntry.DateTime = fileInfo.LastWriteTimeUtc; break; case TimeSetting.Fixed: zipEntry.DateTime = fixedDateTime_; break; default: throw new ZipException("Unhandled time setting in MakeFileEntry"); } zipEntry.Size = fileInfo.Length; flag = true; num = (int)fileInfo.Attributes & getAttributes_; } else if (timeSetting_ == TimeSetting.Fixed) { zipEntry.DateTime = fixedDateTime_; } if (flag) { num |= setAttributes_; zipEntry.ExternalFileAttributes = num; } return zipEntry; } public ZipEntry MakeDirectoryEntry(string directoryName) { return MakeDirectoryEntry(directoryName, useFileSystem: true); } public ZipEntry MakeDirectoryEntry(string directoryName, bool useFileSystem) { ZipEntry zipEntry = new ZipEntry(nameTransform_.TransformDirectory(directoryName)); zipEntry.IsUnicodeText = isUnicodeText_; zipEntry.Size = 0L; int num = 0; DirectoryInfo directoryInfo = null; if (useFileSystem) { directoryInfo = new DirectoryInfo(directoryName); } if (directoryInfo != null && directoryInfo.Exists) { switch (timeSetting_) { case TimeSetting.CreateTime: zipEntry.DateTime = directoryInfo.CreationTime; break; case TimeSetting.CreateTimeUtc: zipEntry.DateTime = directoryInfo.CreationTimeUtc; break; case TimeSetting.LastAccessTime: zipEntry.DateTime = directoryInfo.LastAccessTime; break; case TimeSetting.LastAccessTimeUtc: zipEntry.DateTime = directoryInfo.LastAccessTimeUtc; break; case TimeSetting.LastWriteTime: zipEntry.DateTime = directoryInfo.LastWriteTime; break; case TimeSetting.LastWriteTimeUtc: zipEntry.DateTime = directoryInfo.LastWriteTimeUtc; break; case TimeSetting.Fixed: zipEntry.DateTime = fixedDateTime_; break; default: throw new ZipException("Unhandled time setting in MakeDirectoryEntry"); } num = (int)directoryInfo.Attributes & getAttributes_; } else if (timeSetting_ == TimeSetting.Fixed) { zipEntry.DateTime = fixedDateTime_; } num |= setAttributes_ | 0x10; zipEntry.ExternalFileAttributes = num; return zipEntry; } } [Serializable] public class ZipException : SharpZipBaseException { public ZipException() { } public ZipException(string message) : base(message) { } public ZipException(string message, Exception innerException) : base(message, innerException) { } protected ZipException(SerializationInfo info, StreamingContext context) : base(info, context) { } } public interface ITaggedData { ushort TagID { get; } void SetData(byte[] data, int offset, int count); byte[] GetData(); } public class RawTaggedData : ITaggedData { private ushort _tag; private byte[] _data; public ushort TagID { get { return _tag; } set { _tag = value; } } public byte[] Data { get { return _data; } set { _data = value; } } public RawTaggedData(ushort tag) { _tag = tag; } public void SetData(byte[] data, int offset, int count) { if (data == null) { throw new ArgumentNullException("data"); } _data = new byte[count]; Array.Copy(data, offset, _data, 0, count); } public byte[] GetData() { return _data; } } public class ExtendedUnixData : ITaggedData { [Flags] public enum Flags : byte { ModificationTime = 1, AccessTime = 2, CreateTime = 4 } 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); public ushort TagID => 21589; public DateTime ModificationTime { get { return _modificationTime; } set { if (!IsValidValue(value)) { throw new ArgumentOutOfRangeException("value"); } _flags |= Flags.ModificationTime; _modificationTime = value; } } public DateTime AccessTime { get { return _lastAccessTime; } set { if (!IsValidValue(value)) { throw new ArgumentOutOfRangeException("value"); } _flags |= Flags.AccessTime; _lastAccessTime = value; } } public DateTime CreateTime { get { return _createTime; } set { if (!IsValidValue(value)) { throw new ArgumentOutOfRangeException("value"); } _flags |= Flags.CreateTime; _createTime = value; } } public Flags Include { get { return _flags; } set { _flags = value; } } public void SetData(byte[] data, int index, int count) { using MemoryStream memoryStream = new MemoryStream(data, index, count, writable: false); _flags = (Flags)memoryStream.ReadByte(); if ((_flags & Flags.ModificationTime) != 0) { int seconds = memoryStream.ReadLEInt(); _modificationTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + new TimeSpan(0, 0, 0, seconds, 0); if (count <= 5) { return; } } if ((_flags & Flags.AccessTime) != 0) { int seconds2 = memoryStream.ReadLEInt(); _lastAccessTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + new TimeSpan(0, 0, 0, seconds2, 0); } if ((_flags & Flags.CreateTime) != 0) { int seconds3 = memoryStream.ReadLEInt(); _createTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + new TimeSpan(0, 0, 0, seconds3, 0); } } public byte[] GetData() { using MemoryStream memoryStream = new MemoryStream(); memoryStream.WriteByte((byte)_flags); if ((_flags & Flags.ModificationTime) != 0) { int value = (int)(_modificationTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; memoryStream.WriteLEInt(value); } if ((_flags & Flags.AccessTime) != 0) { int value2 = (int)(_lastAccessTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; memoryStream.WriteLEInt(value2); } if ((_flags & Flags.CreateTime) != 0) { int value3 = (int)(_createTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; memoryStream.WriteLEInt(value3); } return memoryStream.ToArray(); } public static bool IsValidValue(DateTime value) { if (!(value >= new DateTime(1901, 12, 13, 20, 45, 52))) { return value <= new DateTime(2038, 1, 19, 3, 14, 7); } return true; } } public class NTTaggedData : ITaggedData { private DateTime _lastAccessTime = DateTime.FromFileTimeUtc(0L); private DateTime _lastModificationTime = DateTime.FromFileTimeUtc(0L); private DateTime _createTime = DateTime.FromFileTimeUtc(0L); public ushort TagID => 10; public DateTime LastModificationTime { get { return _lastModificationTime; } set { if (!IsValidValue(value)) { throw new ArgumentOutOfRangeException("value"); } _lastModificationTime = value; } } public DateTime CreateTime { get { return _createTime; } set { if (!IsValidValue(value)) { throw new ArgumentOutOfRangeException("value"); } _createTime = value; } } public DateTime LastAccessTime { get { return _lastAccessTime; } set { if (!IsValidValue(value)) { throw new ArgumentOutOfRangeException("value"); } _lastAccessTime = value; } } public void SetData(byte[] data, int index, int count) { using MemoryStream memoryStream = new MemoryStream(data, index, count, writable: false); memoryStream.ReadLEInt(); while (memoryStream.Position < memoryStream.Length) { int num = memoryStream.ReadLEShort(); int num2 = memoryStream.ReadLEShort(); if (num == 1) { if (num2 >= 24) { long fileTime = memoryStream.ReadLELong(); _lastModificationTime = DateTime.FromFileTimeUtc(fileTime); long fileTime2 = memoryStream.ReadLELong(); _lastAccessTime = DateTime.FromFileTimeUtc(fileTime2); long fileTime3 = memoryStream.ReadLELong(); _createTime = DateTime.FromFileTimeUtc(fileTime3); } break; } memoryStream.Seek(num2, SeekOrigin.Current); } } public byte[] GetData() { using MemoryStream memoryStream = new MemoryStream(); memoryStream.WriteLEInt(0); memoryStream.WriteLEShort(1); memoryStream.WriteLEShort(24); memoryStream.WriteLELong(_lastModificationTime.ToFileTimeUtc()); memoryStream.WriteLELong(_lastAccessTime.ToFileTimeUtc()); memoryStream.WriteLELong(_createTime.ToFileTimeUtc()); return memoryStream.ToArray(); } public static bool IsValidValue(DateTime value) { bool result = true; try { value.ToFileTimeUtc(); } catch { result = false; } return result; } } internal interface ITaggedDataFactory { ITaggedData Create(short tag, byte[] data, int offset, int count); } public sealed class ZipExtraData : IDisposable { private int _index; private int _readValueStart; private int _readValueLength; private MemoryStream _newEntry; private byte[] _data; public int Length => _data.Length; public int ValueLength => _readValueLength; public int CurrentReadIndex => _index; 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; } } public ZipExtraData() { Clear(); } public ZipExtraData(byte[] data) { if (data == null) { _data = Empty.Array(); } else { _data = data; } } public byte[] GetEntryData() { if (Length > 65535) { throw new ZipException("Data exceeds maximum length"); } return (byte[])_data.Clone(); } public void Clear() { if (_data == null || _data.Length != 0) { _data = Empty.Array(); } } public Stream GetStreamForTag(int tag) { Stream result = null; if (Find(tag)) { result = new MemoryStream(_data, _index, _readValueLength, writable: false); } return result; } public T GetData() where T : class, ITaggedData, new() { T val = new T(); if (Find(val.TagID)) { val.SetData(_data, _readValueStart, _readValueLength); return val; } return null; } public bool Find(int headerID) { _readValueStart = _data.Length; _readValueLength = 0; _index = 0; int num = _readValueStart; int num2 = headerID - 1; while (num2 != headerID && _index < _data.Length - 3) { num2 = ReadShortInternal(); num = ReadShortInternal(); if (num2 != headerID) { _index += num; } } int num3; if (num2 == headerID) { num3 = ((_index + num <= _data.Length) ? 1 : 0); if (num3 != 0) { _readValueStart = _index; _readValueLength = num; } } else { num3 = 0; } return (byte)num3 != 0; } public void AddEntry(ITaggedData taggedData) { if (taggedData == null) { throw new ArgumentNullException("taggedData"); } AddEntry(taggedData.TagID, taggedData.GetData()); } public void AddEntry(int headerID, byte[] fieldData) { if (headerID > 65535 || headerID < 0) { throw new ArgumentOutOfRangeException("headerID"); } int num = ((fieldData != null) ? fieldData.Length : 0); if (num > 65535) { throw new ArgumentOutOfRangeException("fieldData", "exceeds maximum length"); } int num2 = _data.Length + num + 4; if (Find(headerID)) { num2 -= ValueLength + 4; } if (num2 > 65535) { throw new ZipException("Data exceeds maximum length"); } Delete(headerID); byte[] array = new byte[num2]; _data.CopyTo(array, 0); int index = _data.Length; _data = array; SetShort(ref index, headerID); SetShort(ref index, num); fieldData?.CopyTo(array, index); } public void StartNewEntry() { _newEntry = new MemoryStream(); } public void AddNewEntry(int headerID) { byte[] fieldData = _newEntry.ToArray(); _newEntry = null; AddEntry(headerID, fieldData); } public void AddData(byte data) { _newEntry.WriteByte(data); } public void AddData(byte[] data) { if (data == null) { throw new ArgumentNullException("data"); } _newEntry.Write(data, 0, data.Length); } public void AddLeShort(int toAdd) { _newEntry.WriteByte((byte)toAdd); _newEntry.WriteByte((byte)(toAdd >> 8)); } public void AddLeInt(int toAdd) { AddLeShort((short)toAdd); AddLeShort((short)(toAdd >> 16)); } public void AddLeLong(long toAdd) { AddLeInt((int)(toAdd & 0xFFFFFFFFu)); AddLeInt((int)(toAdd >> 32)); } public bool Delete(int headerID) { bool result = false; if (Find(headerID)) { result = true; int num = _readValueStart - 4; byte[] array = new byte[_data.Length - (ValueLength + 4)]; Array.Copy(_data, 0, array, 0, num); int num2 = num + ValueLength + 4; Array.Copy(_data, num2, array, num, _data.Length - num2); _data = array; } return result; } public long ReadLong() { ReadCheck(8); return (ReadInt() & 0xFFFFFFFFu) | ((long)ReadInt() << 32); } 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; } public int ReadShort() { ReadCheck(2); int result = _data[_index] + (_data[_index + 1] << 8); _index += 2; return result; } public int ReadByte() { int result = -1; if (_index < _data.Length && _readValueStart + _readValueLength > _index) { result = _data[_index]; _index++; } return result; } 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"); } } 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; } public void Dispose() { if (_newEntry != null) { _newEntry.Dispose(); } } } public class KeysRequiredEventArgs : EventArgs { private readonly string fileName; private byte[] key; public string FileName => fileName; public byte[] Key { get { return key; } set { key = value; } } public KeysRequiredEventArgs(string name) { fileName = name; } public KeysRequiredEventArgs(string name, byte[] keyValue) { fileName = name; key = keyValue; } } public enum TestStrategy { FindFirstError, FindAllErrors } public enum TestOperation { Initialising, EntryHeader, EntryData, EntryComplete, MiscellaneousTests, Complete } public class TestStatus { private readonly ZipFile file_; private ZipEntry entry_; private bool entryValid_; private int errorCount_; private long bytesTested_; private TestOperation operation_; public TestOperation Operation => operation_; public ZipFile File => file_; public ZipEntry Entry => entry_; public int ErrorCount => errorCount_; public long BytesTested => bytesTested_; public bool EntryValid => entryValid_; public TestStatus(ZipFile file) { file_ = file; } internal void AddError() { errorCount_++; entryValid_ = false; } internal void SetOperation(TestOperation operation) { operation_ = operation; } internal void SetEntry(ZipEntry entry) { entry_ = entry; entryValid_ = true; bytesTested_ = 0L; } internal void SetBytesTested(long value) { bytesTested_ = value; } } public delegate void ZipTestResultHandler(TestStatus status, string message); public enum FileUpdateMode { Safe, Direct } public class ZipFile : IEnumerable, IDisposable { public delegate void KeysRequiredEventHandler(object sender, KeysRequiredEventArgs e); [Flags] private enum HeaderTest { None = 0, Extract = 1, Header = 2 } private enum UpdateCommand { Copy, Modify, Add } private class UpdateComparer : IComparer { public int Compare(ZipUpdate x, ZipUpdate y) { int num; if (x == null) { num = ((y != null) ? (-1) : 0); } else if (y == null) { num = 1; } else { int num2 = ((x.Command != UpdateCommand.Copy && x.Command != UpdateCommand.Modify) ? 1 : 0); int num3 = ((y.Command != UpdateCommand.Copy && y.Command != UpdateCommand.Modify) ? 1 : 0); num = num2 - num3; if (num == 0) { long num4 = x.Entry.Offset - y.Entry.Offset; num = ((num4 < 0) ? (-1) : ((num4 != 0L) ? 1 : 0)); } } return num; } } private class ZipUpdate { private ZipEntry entry_; private ZipEntry outEntry_; private readonly UpdateCommand command_; private IStaticDataSource dataSource_; private readonly string filename_; private long sizePatchOffset_ = -1L; private long crcPatchOffset_ = -1L; private long _offsetBasedSize = -1L; public ZipEntry Entry => entry_; public ZipEntry OutEntry { get { if (outEntry_ == null) { outEntry_ = (ZipEntry)entry_.Clone(); } return outEntry_; } } public UpdateCommand Command => command_; public string Filename => filename_; public long SizePatchOffset { get { return sizePatchOffset_; } set { sizePatchOffset_ = value; } } public long CrcPatchOffset { get { return crcPatchOffset_; } set { crcPatchOffset_ = value; } } public long OffsetBasedSize { get { return _offsetBasedSize; } set { _offsetBasedSize = value; } } 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) { } [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"); } public ZipUpdate(UpdateCommand command, ZipEntry entry) { command_ = command; entry_ = (ZipEntry)entry.Clone(); } public ZipUpdate(ZipEntry entry) : this(UpdateCommand.Copy, entry) { } public Stream GetSource() { Stream result = null; if (dataSource_ != null) { result = dataSource_.GetSource(); } return result; } } private class ZipString { private string comment_; private byte[] rawComment_; private readonly bool isSourceString_; private readonly Encoding _encoding; public bool IsSourceString => isSourceString_; public int RawLength { get { MakeBytesAvailable(); return rawComment_.Length; } } public byte[] RawComment { get { MakeBytesAvailable(); return (byte[])rawComment_.Clone(); } } public ZipString(string comment, Encoding encoding) { comment_ = comment; isSourceString_ = true; _encoding = encoding; } public ZipString(byte[] rawString, Encoding encoding) { rawComment_ = rawString; _encoding = encoding; } 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_); } } public static implicit operator string(ZipString zipString) { zipString.MakeTextAvailable(); return zipString.comment_; } } private class ZipEntryEnumerator : IEnumerator { private ZipEntry[] array; private int index = -1; public object Current => array[index]; public ZipEntryEnumerator(ZipEntry[] entries) { array = entries; } public void Reset() { index = -1; } public bool MoveNext() { return ++index < array.Length; } } private class UncompressedStream : Stream { private readonly Stream baseStream_; public override bool CanRead => false; public override bool CanWrite => baseStream_.CanWrite; public override bool CanSeek => false; public override long Length => 0L; public override long Position { get { return baseStream_.Position; } set { throw new NotImplementedException(); } } public UncompressedStream(Stream baseStream) { baseStream_ = baseStream; } public override void Flush() { baseStream_.Flush(); } public override int Read(byte[] buffer, int offset, int count) { return 0; } public override long Seek(long offset, SeekOrigin origin) { return 0L; } public override void SetLength(long value) { } public override void Write(byte[] buffer, int offset, int count) { baseStream_.Write(buffer, offset, count); } } private class PartialInputStream : Stream { private ZipFile zipFile_; private Stream baseStream_; private readonly long start_; private readonly long length_; private long readPos_; private readonly long end_; public override long Position { get { return readPos_ - start_; } set { long num = start_ + value; if (num < start_) { throw new ArgumentException("Negative position is invalid"); } if (num > end_) { throw new InvalidOperationException("Cannot seek past end"); } readPos_ = num; } } public override long Length => length_; public override bool CanWrite => false; public override bool CanSeek => true; public override bool CanRead => true; public override bool CanTimeout => baseStream_.CanTimeout; public PartialInputStream(ZipFile zipFile, long start, long length) { start_ = start; length_ = length; zipFile_ = zipFile; baseStream_ = zipFile_.baseStream_; readPos_ = start; end_ = start + length; } public override int ReadByte() { if (readPos_ >= end_) { return -1; } lock (baseStream_) { baseStream_.Seek(readPos_++, SeekOrigin.Begin); return baseStream_.ReadByte(); } } 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; } } if (baseStream_.Position != readPos_) { baseStream_.Seek(readPos_, SeekOrigin.Begin); } int num = baseStream_.Read(buffer, offset, count); if (num > 0) { readPos_ += num; } return num; } } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override long Seek(long offset, SeekOrigin origin) { long num = readPos_; switch (origin) { case SeekOrigin.Begin: num = start_ + offset; break; case SeekOrigin.Current: num = readPos_ + offset; break; case SeekOrigin.End: num = end_ + offset; break; } if (num < start_) { throw new ArgumentException("Negative position is invalid"); } if (num > end_) { throw new IOException("Cannot seek past end"); } readPos_ = num; return readPos_; } public override void Flush() { } } public KeysRequiredEventHandler KeysRequired; private const int DefaultBufferSize = 4096; 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(); private UseZip64 useZip64_ = UseZip64.Dynamic; private List updates_; private long updateCount_; private Dictionary updateIndex_; private IArchiveStorage archiveStorage_; private IDynamicDataSource updateDataSource_; private bool contentsEdited_; private int bufferSize_ = 4096; private byte[] copyBuffer_; private ZipString newComment_; private bool commentEdited_; private IEntryFactory updateEntryFactory_ = new ZipEntryFactory(); private byte[] Key { get { return key; } set { key = value; } } public string Password { set { if (string.IsNullOrEmpty(value)) { key = null; } else { key = PkzipClassic.GenerateKeys(ZipCryptoEncoding.GetBytes(value)); } rawPassword_ = value; } } private bool HaveKeys => key != null; public bool IsStreamOwner { get { return isStreamOwner; } set { isStreamOwner = value; } } public bool IsEmbeddedArchive => offsetOfFirstEntry > 0; public bool IsNewArchive => isNewArchive_; public string ZipFileComment => comment_; public string Name => name_; [Obsolete("Use the Count property instead")] public int Size => entries_.Length; public long Count => entries_.Length; [IndexerName("EntryByIndex")] public ZipEntry this[int index] => (ZipEntry)entries_[index].Clone(); public Encoding ZipCryptoEncoding { get { return _stringCodec.ZipCryptoEncoding; } set { _stringCodec.ZipCryptoEncoding = value; } } public StringCodec StringCodec { get { return _stringCodec; } set { _stringCodec = value; } } public INameTransform NameTransform { get { return updateEntryFactory_.NameTransform; } set { updateEntryFactory_.NameTransform = value; } } public IEntryFactory EntryFactory { get { return updateEntryFactory_; } set { if (value == null) { updateEntryFactory_ = new ZipEntryFactory(); } else { updateEntryFactory_ = value; } } } public int BufferSize { get { return bufferSize_; } set { if (value < 1024) { throw new ArgumentOutOfRangeException("value", "cannot be below 1024"); } if (bufferSize_ != value) { bufferSize_ = value; copyBuffer_ = null; } } } public bool IsUpdating => updates_ != null; public UseZip64 UseZip64 { get { return useZip64_; } set { useZip64_ = value; } } public bool SkipLocalEntryTestsOnLocate { get; set; } private void OnKeysRequired(string fileName) { if (KeysRequired != null) { KeysRequiredEventArgs e = new KeysRequiredEventArgs(fileName, key); KeysRequired(this, e); key = e.Key; } } public ZipFile(string name, StringCodec stringCodec = null) { name_ = name ?? throw new ArgumentNullException("name"); baseStream_ = File.Open(name, FileMode.Open, FileAccess.Read, FileShare.Read); isStreamOwner = true; if (stringCodec != null) { _stringCodec = stringCodec; } try { ReadEntries(); } catch { DisposeInternal(disposing: true); throw; } } public ZipFile(FileStream file) : this(file, leaveOpen: false) { } public ZipFile(FileStream file, bool leaveOpen) { if (file == null) { throw new ArgumentNullException("file"); } if (!file.CanSeek) { throw new ArgumentException("Stream is not seekable", "file"); } baseStream_ = file; name_ = file.Name; isStreamOwner = !leaveOpen; try { ReadEntries(); } catch { DisposeInternal(disposing: true); throw; } } public ZipFile(Stream stream) : this(stream, leaveOpen: false) { } public ZipFile(Stream stream, bool leaveOpen) { if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanSeek) { throw new ArgumentException("Stream is not seekable", "stream"); } baseStream_ = stream; isStreamOwner = !leaveOpen; if (baseStream_.Length > 0) { try { ReadEntries(); return; } catch { DisposeInternal(disposing: true); throw; } } entries_ = Empty.Array(); isNewArchive_ = true; } internal ZipFile() { entries_ = Empty.Array(); isNewArchive_ = true; } ~ZipFile() { Dispose(disposing: false); } public void Close() { DisposeInternal(disposing: true); GC.SuppressFinalize(this); } public static ZipFile Create(string fileName) { if (fileName == null) { throw new ArgumentNullException("fileName"); } FileStream fileStream = File.Create(fileName); return new ZipFile { name_ = fileName, baseStream_ = fileStream, isStreamOwner = true }; } public static ZipFile Create(Stream outStream) { if (outStream == null) { throw new ArgumentNullException("outStream"); } if (!outStream.CanWrite) { throw new ArgumentException("Stream is not writeable", "outStream"); } if (!outStream.CanSeek) { throw new ArgumentException("Stream is not seekable", "outStream"); } return new ZipFile { baseStream_ = outStream }; } public IEnumerator GetEnumerator() { if (isDisposed_) { throw new ObjectDisposedException("ZipFile"); } return new ZipEntryEnumerator(entries_); } public int FindEntry(string name, bool ignoreCase) { if (isDisposed_) { throw new ObjectDisposedException("ZipFile"); } 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; } public ZipEntry GetEntry(string name) { if (isDisposed_) { throw new ObjectDisposedException("ZipFile"); } int num = FindEntry(name, ignoreCase: true); if (num < 0) { return null; } return (ZipEntry)entries_[num].Clone(); } public Stream GetInputStream(ZipEntry entry) { if (entry == null) { throw new ArgumentNullException("entry"); } if (isDisposed_) { throw new ObjectDisposedException("ZipFile"); } long num = entry.ZipFileIndex; if (num < 0 || num >= entries_.Length || entries_[num].Name != entry.Name) { num = FindEntry(entry.Name, ignoreCase: true); if (num < 0) { throw new ZipException("Entry cannot be found"); } } return GetInputStream(num); } public Stream GetInputStream(long entryIndex) { if (isDisposed_) { throw new ObjectDisposedException("ZipFile"); } long start = LocateEntry(entries_[entryIndex]); CompressionMethod compressionMethod = entries_[entryIndex].CompressionMethod; Stream stream = new PartialInputStream(this, start, entries_[entryIndex].CompressedSize); if (entries_[entryIndex].IsCrypted) { stream = CreateAndInitDecryptionStream(stream, entries_[entryIndex]); if (stream == null) { throw new ZipException("Unable to decrypt this entry"); } } switch (compressionMethod) { case CompressionMethod.Deflated: stream = new InflaterInputStream(stream, new Inflater(noHeader: true)); break; case CompressionMethod.BZip2: stream = new BZip2InputStream(stream); break; default: throw new ZipException("Unsupported compression method " + compressionMethod); case CompressionMethod.Stored: break; } return stream; } public bool TestArchive(bool testData) { return TestArchive(testData, TestStrategy.FindFirstError, null); } public bool TestArchive(bool testData, TestStrategy strategy, ZipTestResultHandler resultHandler) { if (isDisposed_) { throw new ObjectDisposedException("ZipFile"); } TestStatus testStatus = new TestStatus(this); resultHandler?.Invoke(testStatus, null); HeaderTest tests = (testData ? (HeaderTest.Extract | HeaderTest.Header) : HeaderTest.Header); bool flag = true; try { int num = 0; while (flag && num < Count) { if (resultHandler != null) { testStatus.SetEntry(this[num]); testStatus.SetOperation(TestOperation.EntryHeader); resultHandler(testStatus, null); } try { TestLocalHeader(this[num], tests); } catch (ZipException ex) { testStatus.AddError(); resultHandler?.Invoke(testStatus, "Exception during test - '" + ex.Message + "'"); flag = flag && strategy != TestStrategy.FindFirstError; } if (flag && testData && this[num].IsFile) { bool flag2 = this[num].AESKeySize == 0; if (resultHandler != null) { testStatus.SetOperation(TestOperation.EntryData); resultHandler(testStatus, null); } Crc32 crc = new Crc32(); using (Stream stream = GetInputStream(this[num])) { byte[] array = new byte[4096]; long num2 = 0L; int num3; while ((num3 = stream.Read(array, 0, array.Length)) > 0) { if (flag2) { crc.Update(new ArraySegment(array, 0, num3)); } if (resultHandler != null) { num2 += num3; testStatus.SetBytesTested(num2); resultHandler(testStatus, null); } } } if (flag2 && this[num].Crc != crc.Value) { testStatus.AddError(); resultHandler?.Invoke(testStatus, "CRC mismatch"); flag = flag && strategy != TestStrategy.FindFirstError; } if ((this[num].Flags & 8) != 0) { DescriptorData descriptorData = new DescriptorData(); ZipFormat.ReadDataDescriptor(baseStream_, this[num].LocalHeaderRequiresZip64, descriptorData); if (flag2 && this[num].Crc != descriptorData.Crc) { testStatus.AddError(); resultHandler?.Invoke(testStatus, "Descriptor CRC mismatch"); } if (this[num].CompressedSize != descriptorData.CompressedSize) { testStatus.AddError(); resultHandler?.Invoke(testStatus, "Descriptor compressed size mismatch"); } if (this[num].Size != descriptorData.Size) { testStatus.AddError(); resultHandler?.Invoke(testStatus, "Descriptor size mismatch"); } } } if (resultHandler != null) { testStatus.SetOperation(TestOperation.EntryComplete); resultHandler(testStatus, null); } num++; } if (resultHandler != null) { testStatus.SetOperation(TestOperation.MiscellaneousTests); resultHandler(testStatus, null); } } catch (Exception ex2) { testStatus.AddError(); resultHandler?.Invoke(testStatus, "Exception during test - '" + ex2.Message + "'"); } if (resultHandler != null) { testStatus.SetOperation(TestOperation.Complete); testStatus.SetEntry(null); resultHandler(testStatus, null); } return testStatus.ErrorCount == 0; } private long TestLocalHeader(ZipEntry entry, HeaderTest tests) { lock (baseStream_) { bool num = (tests & HeaderTest.Header) != 0; bool num2 = (tests & HeaderTest.Extract) != 0; long num3 = offsetOfFirstEntry + entry.Offset; baseStream_.Seek(num3, SeekOrigin.Begin); int num4 = (int)ReadLEUint(); if (num4 != 67324752) { throw new ZipException($"Wrong local header signature at 0x{num3:x}, expected 0x{67324752:x8}, actual 0x{num4:x8}"); } short num5 = (short)(ReadLEUshort() & 0xFF); GeneralBitFlags generalBitFlags = (GeneralBitFlags)ReadLEUshort(); CompressionMethod compressionMethod = (CompressionMethod)ReadLEUshort(); short num6 = (short)ReadLEUshort(); short num7 = (short)ReadLEUshort(); uint num8 = ReadLEUint(); long num9 = ReadLEUint(); long num10 = ReadLEUint(); int num11 = ReadLEUshort(); int num12 = ReadLEUshort(); byte[] array = new byte[num11]; StreamUtils.ReadFully(baseStream_, array); byte[] array2 = new byte[num12]; StreamUtils.ReadFully(baseStream_, array2); ZipExtraData zipExtraData = new ZipExtraData(array2); if (zipExtraData.Find(1)) { num10 = zipExtraData.ReadLong(); num9 = zipExtraData.ReadLong(); if (generalBitFlags.HasAny(GeneralBitFlags.Descriptor)) { if (num10 != 0L && num10 != entry.Size) { throw new ZipException("Size invalid for descriptor"); } if (num9 != 0L && num9 != entry.CompressedSize) { throw new ZipException("Compressed size invalid for descriptor"); } } } else if (num5 >= 45 && ((int)num10 == -1 || (int)num9 == -1)) { throw new ZipException("Required Zip64 extended information missing"); } if (num2 && entry.IsFile) { if (!entry.IsCompressionMethodSupported()) { throw new ZipException("Compression method not supported"); } if (num5 > 51 || (num5 > 20 && num5 < 45)) { throw new ZipException($"Version required to extract this entry not supported ({num5})"); } if (generalBitFlags.HasAny(GeneralBitFlags.Patched | GeneralBitFlags.StrongEncryption | GeneralBitFlags.EnhancedCompress | GeneralBitFlags.HeaderMasked)) { throw new ZipException($"The library does not support the zip features required to extract this entry ({generalBitFlags & (GeneralBitFlags.Patched | GeneralBitFlags.StrongEncryption | GeneralBitFlags.EnhancedCompress | GeneralBitFlags.HeaderMasked):F})"); } } if (num) { if (num5 <= 63 && num5 != 10 && num5 != 11 && num5 != 20 && num5 != 21 && num5 != 25 && num5 != 27 && num5 != 45 && num5 != 46 && num5 != 50 && num5 != 51 && num5 != 52 && num5 != 61 && num5 != 62 && num5 != 63) { throw new ZipException($"Version required to extract this entry is invalid ({num5})"); } Encoding encoding = _stringCodec.ZipInputEncoding(generalBitFlags); if (generalBitFlags.HasAny(GeneralBitFlags.ReservedPKware4 | GeneralBitFlags.ReservedPkware14 | GeneralBitFlags.ReservedPkware15)) { throw new ZipException("Reserved bit flags cannot be set."); } if (generalBitFlags.HasAny(GeneralBitFlags.Encrypted) && num5 < 20) { throw new ZipException($"Version required to extract this entry is too low for encryption ({num5})"); } if (generalBitFlags.HasAny(GeneralBitFlags.StrongEncryption)) { if (!generalBitFlags.HasAny(GeneralBitFlags.Encrypted)) { throw new ZipException("Strong encryption flag set but encryption flag is not set"); } if (num5 < 50) { throw new ZipException($"Version required to extract this entry is too low for encryption ({num5})"); } } if (generalBitFlags.HasAny(GeneralBitFlags.Patched) && num5 < 27) { throw new ZipException($"Patched data requires higher version than ({num5})"); } if (generalBitFlags != (GeneralBitFlags)entry.Flags) { throw new ZipException($"Central header/local header flags mismatch ({(GeneralBitFlags)entry.Flags:F} vs {generalBitFlags:F})"); } if (entry.CompressionMethodForHeader != compressionMethod) { throw new ZipException($"Central header/local header compression method mismatch ({entry.CompressionMethodForHeader:G} vs {compressionMethod:G})"); } if (entry.Version != num5) { throw new ZipException("Extract version mismatch"); } if (generalBitFlags.HasAny(GeneralBitFlags.StrongEncryption) && num5 < 62) { throw new ZipException("Strong encryption flag set but version not high enough"); } if (generalBitFlags.HasAny(GeneralBitFlags.HeaderMasked) && (num6 != 0 || num7 != 0)) { throw new ZipException("Header masked set but date/time values non-zero"); } if (!generalBitFlags.HasAny(GeneralBitFlags.Descriptor) && num8 != (uint)entry.Crc) { throw new ZipException("Central header/local header crc mismatch"); } if (num10 == 0L && num9 == 0L && num8 != 0) { throw new ZipException("Invalid CRC for empty entry"); } if (entry.Name.Length > num11) { throw new ZipException("File name length mismatch"); } string text = encoding.GetString(array); if (text != entry.Name) { throw new ZipException("Central header and local header file name mismatch"); } if (entry.IsDirectory) { if (num10 > 0) { throw new ZipException("Directory cannot have size"); } if (entry.IsCrypted) { if (num9 > entry.EncryptionOverheadSize + 2) { throw new ZipException("Directory compressed size invalid"); } } else if (num9 > 2) { throw new ZipException("Directory compressed size invalid"); } } if (!ZipNameTransform.IsValidName(text, relaxed: true)) { throw new ZipException("Name is invalid"); } } if (!generalBitFlags.HasAny(GeneralBitFlags.Descriptor) || ((num10 > 0 || num9 > 0) && entry.Size > 0)) { if (num10 != 0L && num10 != entry.Size) { throw new ZipException($"Size mismatch between central header ({entry.Size}) and local header ({num10})"); } if (num9 != 0L && num9 != entry.CompressedSize && num9 != uint.MaxValue && num9 != -1) { throw new ZipException($"Compressed size mismatch between central header({entry.CompressedSize}) and local header({num9})"); } } int num13 = num11 + num12; return offsetOfFirstEntry + entry.Offset + 30 + num13; } } 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("archiveStorage"); updateDataSource_ = dataSource ?? throw new ArgumentNullException("dataSource"); updateIndex_ = new Dictionary(); updates_ = new List(entries_.Length); ZipEntry[] array = entries_; foreach (ZipEntry zipEntry in array) { int count = updates_.Count; updates_.Add(new ZipUpdate(zipEntry)); updateIndex_.Add(zipEntry.Name, count); } updates_.Sort(new UpdateComparer()); int num = 0; foreach (ZipUpdate item in updates_) { if (num == updates_.Count - 1) { break; } item.OffsetBasedSize = updates_[num + 1].Entry.Offset - item.Entry.Offset; num++; } updateCount_ = updates_.Count; contentsEdited_ = false; commentEdited_ = false; newComment_ = null; } public void BeginUpdate(IArchiveStorage archiveStorage) { BeginUpdate(archiveStorage, new DynamicDiskDataSource()); } public void BeginUpdate() { if (Name == null) { BeginUpdate(new MemoryArchiveStorage(), new DynamicDiskDataSource()); } else { BeginUpdate(new DiskArchiveStorage(this), new DynamicDiskDataSource()); } } public void CommitUpdate() { if (isDisposed_) { throw new ObjectDisposedException("ZipFile"); } CheckUpdating(); try { updateIndex_.Clear(); updateIndex_ = null; if (contentsEdited_) { RunUpdates(); } else if (commentEdited_) { UpdateCommentOnly(); } else if (entries_.Length == 0) { byte[] comment = ((newComment_ != null) ? newComment_.RawComment : _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment_)); ZipFormat.WriteEndOfCentralDirectory(baseStream_, 0L, 0L, 0L, comment); } } finally { PostUpdateCleanup(); } } public void AbortUpdate() { PostUpdateCleanup(); } public void SetComment(string comment) { if (isDisposed_) { throw new ObjectDisposedException("ZipFile"); } CheckUpdating(); newComment_ = new ZipString(comment, _stringCodec.ZipArchiveCommentEncoding); if (newComment_.RawLength > 65535) { newComment_ = null; throw new ZipException("Comment length exceeds maximum - 65535"); } commentEdited_ = true; } private void AddUpdate(ZipUpdate update) { contentsEdited_ = true; int num = FindExistingUpdate(update.Entry.Name, isEntryName: true); if (num >= 0) { if (updates_[num] == null) { updateCount_++; } updates_[num] = update; } else { num = updates_.Count; updates_.Add(update); updateCount_++; updateIndex_.Add(update.Entry.Name, num); } } public void Add(string fileName, CompressionMethod compressionMethod, bool useUnicodeText) { if (fileName == null) { throw new ArgumentNullException("fileName"); } if (isDisposed_) { throw new ObjectDisposedException("ZipFile"); } CheckSupportedCompressionMethod(compressionMethod); CheckUpdating(); contentsEdited_ = true; ZipEntry zipEntry = EntryFactory.MakeFileEntry(fileName); zipEntry.IsUnicodeText = useUnicodeText; zipEntry.CompressionMethod = compressionMethod; AddUpdate(new ZipUpdate(fileName, zipEntry)); } public void Add(string fileName, CompressionMethod compressionMethod) { if (fileName == null) { throw new ArgumentNullException("fileName"); } CheckSupportedCompressionMethod(compressionMethod); CheckUpdating(); contentsEdited_ = true; ZipEntry zipEntry = EntryFactory.MakeFileEntry(fileName); zipEntry.CompressionMethod = compressionMethod; AddUpdate(new ZipUpdate(fileName, zipEntry)); } public void Add(string fileName) { if (fileName == null) { throw new ArgumentNullException("fileName"); } CheckUpdating(); AddUpdate(new ZipUpdate(fileName, EntryFactory.MakeFileEntry(fileName))); } public void Add(string fileName, string entryName) { if (fileName == null) { throw new ArgumentNullException("fileName"); } if (entryName == null) { throw new ArgumentNullException("entryName"); } CheckUpdating(); AddUpdate(new ZipUpdate(fileName, EntryFactory.MakeFileEntry(fileName, entryName, useFileSystem: true))); } public void Add(IStaticDataSource dataSource, string entryName) { if (dataSource == null) { throw new ArgumentNullException("dataSource"); } if (entryName == null) { throw new ArgumentNullException("entryName"); } CheckUpdating(); AddUpdate(new ZipUpdate(dataSource, EntryFactory.MakeFileEntry(entryName, useFileSystem: false))); } public void Add(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod) { if (dataSource == null) { throw new ArgumentNullException("dataSource"); } if (entryName == null) { throw new ArgumentNullException("entryName"); } CheckSupportedCompressionMethod(compressionMethod); CheckUpdating(); ZipEntry zipEntry = EntryFactory.MakeFileEntry(entryName, useFileSystem: false); zipEntry.CompressionMethod = compressionMethod; AddUpdate(new ZipUpdate(dataSource, zipEntry)); } public void Add(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod, bool useUnicodeText) { if (dataSource == null) { throw new ArgumentNullException("dataSource"); } if (entryName == null) { throw new ArgumentNullException("entryName"); } CheckSupportedCompressionMethod(compressionMethod); CheckUpdating(); ZipEntry zipEntry = EntryFactory.MakeFileEntry(entryName, useFileSystem: false); zipEntry.IsUnicodeText = useUnicodeText; zipEntry.CompressionMethod = compressionMethod; AddUpdate(new ZipUpdate(dataSource, zipEntry)); } public void Add(ZipEntry entry) { if (entry == null) { throw new ArgumentNullException("entry"); } CheckUpdating(); if (entry.Size != 0L || entry.CompressedSize != 0L) { throw new ZipException("Entry cannot have any data"); } AddUpdate(new ZipUpdate(UpdateCommand.Add, entry)); } public void Add(IStaticDataSource dataSource, ZipEntry entry) { if (entry == null) { throw new ArgumentNullException("entry"); } if (dataSource == null) { throw new ArgumentNullException("dataSource"); } if (entry.AESKeySize > 0) { throw new NotSupportedException("Creation of AES encrypted entries is not supported"); } CheckSupportedCompressionMethod(entry.CompressionMethod); CheckUpdating(); AddUpdate(new ZipUpdate(dataSource, entry)); } public void AddDirectory(string directoryName) { if (directoryName == null) { throw new ArgumentNullException("directoryName"); } CheckUpdating(); ZipEntry entry = EntryFactory.MakeDirectoryEntry(directoryName); AddUpdate(new ZipUpdate(UpdateCommand.Add, entry)); } private static void CheckSupportedCompressionMethod(CompressionMethod compressionMethod) { if (compressionMethod != CompressionMethod.Deflated && compressionMethod != CompressionMethod.Stored && compressionMethod != CompressionMethod.BZip2) { throw new NotImplementedException("Compression method not supported"); } } public bool Delete(string fileName) { if (fileName == null) { throw new ArgumentNullException("fileName"); } CheckUpdating(); bool flag = false; int num = FindExistingUpdate(fileName); if (num >= 0 && updates_[num] != null) { flag = true; contentsEdited_ = true; updates_[num] = null; updateCount_--; return flag; } throw new ZipException("Cannot find entry to delete"); } public void Delete(ZipEntry entry) { if (entry == null) { throw new ArgumentNullException("entry"); } CheckUpdating(); int num = FindExistingUpdate(entry); if (num >= 0) { contentsEdited_ = true; updates_[num] = null; updateCount_--; return; } throw new ZipException("Cannot find entry to delete"); } private void WriteLEShort(int value) { baseStream_.WriteByte((byte)(value & 0xFF)); baseStream_.WriteByte((byte)((value >> 8) & 0xFF)); } private void WriteLEUshort(ushort value) { baseStream_.WriteByte((byte)(value & 0xFF)); baseStream_.WriteByte((byte)(value >> 8)); } private void WriteLEInt(int value) { WriteLEShort(value & 0xFFFF); WriteLEShort(value >> 16); } private void WriteLEUint(uint value) { WriteLEUshort((ushort)(value & 0xFFFF)); WriteLEUshort((ushort)(value >> 16)); } private void WriteLeLong(long value) { WriteLEInt((int)(value & 0xFFFFFFFFu)); WriteLEInt((int)(value >> 32)); } private void WriteLEUlong(ulong value) { WriteLEUint((uint)(value & 0xFFFFFFFFu)); WriteLEUint((uint)(value >> 32)); } private void WriteLocalEntryHeader(ZipUpdate update) { ZipEntry outEntry = update.OutEntry; outEntry.Offset = baseStream_.Position; if (update.Command != UpdateCommand.Copy) { if (outEntry.CompressionMethod == CompressionMethod.Deflated) { if (outEntry.Size == 0L) { outEntry.CompressedSize = outEntry.Size; outEntry.Crc = 0L; outEntry.CompressionMethod = CompressionMethod.Stored; } } else if (outEntry.CompressionMethod == CompressionMethod.Stored) { outEntry.Flags &= -9; } if (HaveKeys) { outEntry.IsCrypted = true; if (outEntry.Crc < 0) { outEntry.Flags |= 8; } } else { outEntry.IsCrypted = false; } switch (useZip64_) { case UseZip64.Dynamic: if (outEntry.Size < 0) { outEntry.ForceZip64(); } break; case UseZip64.On: outEntry.ForceZip64(); break; } } WriteLEInt(67324752); WriteLEShort(outEntry.Version); WriteLEShort(outEntry.Flags); WriteLEShort((byte)outEntry.CompressionMethodForHeader); WriteLEInt((int)outEntry.DosTime); if (!outEntry.HasCrc) { update.CrcPatchOffset = baseStream_.Position; WriteLEInt(0); } else { WriteLEInt((int)outEntry.Crc); } if (outEntry.LocalHeaderRequiresZip64) { WriteLEInt(-1); WriteLEInt(-1); } else { if (outEntry.CompressedSize < 0 || outEntry.Size < 0) { update.SizePatchOffset = baseStream_.Position; } WriteLEInt((int)outEntry.CompressedSize); WriteLEInt((int)outEntry.Size); } byte[] bytes = _stringCodec.ZipInputEncoding(outEntry.Flags).GetBytes(outEntry.Name); if (bytes.Length > 65535) { throw new ZipException("Entry name too long."); } ZipExtraData zipExtraData = new ZipExtraData(outEntry.ExtraData); if (outEntry.LocalHeaderRequiresZip64) { zipExtraData.StartNewEntry(); zipExtraData.AddLeLong(outEntry.Size); zipExtraData.AddLeLong(outEntry.CompressedSize); zipExtraData.AddNewEntry(1); } else { zipExtraData.Delete(1); } outEntry.ExtraData = zipExtraData.GetEntryData(); WriteLEShort(bytes.Length); WriteLEShort(outEntry.ExtraData.Length); if (bytes.Length != 0) { baseStream_.Write(bytes, 0, bytes.Length); } if (outEntry.LocalHeaderRequiresZip64) { if (!zipExtraData.Find(1)) { throw new ZipException("Internal error cannot find extra data"); } update.SizePatchOffset = baseStream_.Position + zipExtraData.CurrentReadIndex; } if (outEntry.ExtraData.Length != 0) { baseStream_.Write(outEntry.ExtraData, 0, outEntry.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"); } WriteLEInt(33639248); WriteLEShort((entry.HostSystem << 8) | entry.VersionMadeBy); WriteLEShort(entry.Version); WriteLEShort(entry.Flags); WriteLEShort((byte)entry.CompressionMethodForHeader); WriteLEInt((int)entry.DosTime); WriteLEInt((int)entry.Crc); bool flag = false; if (entry.IsZip64Forced() || entry.CompressedSize >= uint.MaxValue) { flag = true; WriteLEInt(-1); } else { WriteLEInt((int)(entry.CompressedSize & 0xFFFFFFFFu)); } bool flag2 = false; if (entry.IsZip64Forced() || entry.Size >= uint.MaxValue) { flag2 = true; WriteLEInt(-1); } else { WriteLEInt((int)entry.Size); } byte[] bytes = _stringCodec.ZipInputEncoding(entry.Flags).GetBytes(entry.Name); if (bytes.Length > 65535) { throw new ZipException("Entry name is too long."); } WriteLEShort(bytes.Length); ZipExtraData zipExtraData = new ZipExtraData(entry.ExtraData); if (entry.CentralHeaderRequiresZip64) { zipExtraData.StartNewEntry(); if (flag2) { zipExtraData.AddLeLong(entry.Size); } if (flag) { zipExtraData.AddLeLong(entry.CompressedSize); } if (entry.Offset >= uint.MaxValue) { zipExtraData.AddLeLong(entry.Offset); } zipExtraData.AddNewEntry(1); } else { zipExtraData.Delete(1); } byte[] entryData = zipExtraData.GetEntryData(); WriteLEShort(entryData.Length); WriteLEShort((entry.Comment != null) ? entry.Comment.Length : 0); WriteLEShort(0); WriteLEShort(0); if (entry.ExternalFileAttributes != -1) { WriteLEInt(entry.ExternalFileAttributes); } else if (entry.IsDirectory) { WriteLEUint(16u); } else { WriteLEUint(0u); } if (entry.Offset >= uint.MaxValue) { WriteLEUint(uint.MaxValue); } else { WriteLEUint((uint)entry.Offset); } if (bytes.Length != 0) { baseStream_.Write(bytes, 0, bytes.Length); } if (entryData.Length != 0) { baseStream_.Write(entryData, 0, entryData.Length); } byte[] array = ((entry.Comment != null) ? Encoding.ASCII.GetBytes(entry.Comment) : Empty.Array()); if (array.Length != 0) { baseStream_.Write(array, 0, array.Length); } return 46 + bytes.Length + entryData.Length + array.Length; } private void PostUpdateCleanup() { updateDataSource_ = null; updates_ = null; updateIndex_ = null; if (archiveStorage_ != null) { archiveStorage_.Dispose(); archiveStorage_ = null; } } private string GetTransformedFileName(string name) { INameTransform nameTransform = NameTransform; if (nameTransform == null) { return name; } return nameTransform.TransformFile(name); } private string GetTransformedDirectoryName(string name) { INameTransform nameTransform = NameTransform; if (nameTransform == null) { return name; } return nameTransform.TransformDirectory(name); } private byte[] GetBuffer() { if (copyBuffer_ == null) { copyBuffer_ = new byte[bufferSize_]; } return copyBuffer_; } private void CopyDescriptorBytes(ZipUpdate update, Stream dest, Stream source) { int num = GetDescriptorSize(update, includingSignature: false); if (num == 0) { return; } byte[] buffer = GetBuffer(); source.Read(buffer, 0, 4); dest.Write(buffer, 0, 4); if (BitConverter.ToUInt32(buffer, 0) != 134695760) { num -= buffer.Length; } while (num > 0) { int count = Math.Min(buffer.Length, num); int num2 = source.Read(buffer, 0, count); if (num2 > 0) { dest.Write(buffer, 0, num2); num -= num2; continue; } 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"); } Crc32 crc = new Crc32(); byte[] buffer = GetBuffer(); long num = bytesToCopy; long num2 = 0L; int num4; do { int num3 = buffer.Length; if (bytesToCopy < num3) { num3 = (int)bytesToCopy; } num4 = source.Read(buffer, 0, num3); if (num4 > 0) { if (updateCrc) { crc.Update(new ArraySegment(buffer, 0, num4)); } destination.Write(buffer, 0, num4); bytesToCopy -= num4; num2 += num4; } } while (num4 > 0 && bytesToCopy > 0); if (num2 != num) { throw new ZipException($"Failed to copy bytes expected {num} read {num2}"); } if (updateCrc) { update.OutEntry.Crc = crc.Value; } } private static int GetDescriptorSize(ZipUpdate update, bool includingSignature) { if (!((GeneralBitFlags)update.Entry.Flags).HasAny(GeneralBitFlags.Descriptor)) { return 0; } int num = (update.Entry.LocalHeaderRequiresZip64 ? 24 : 16); if (!includingSignature) { return num - 4; } return num; } private void CopyDescriptorBytesDirect(ZipUpdate update, Stream stream, ref long destinationPosition, long sourcePosition) { byte[] buffer = GetBuffer(); stream.Position = sourcePosition; stream.Read(buffer, 0, 4); bool includingSignature = BitConverter.ToUInt32(buffer, 0) == 134695760; int num = GetDescriptorSize(update, includingSignature); while (num > 0) { stream.Position = sourcePosition; int num2 = stream.Read(buffer, 0, num); if (num2 > 0) { stream.Position = destinationPosition; stream.Write(buffer, 0, num2); num -= num2; destinationPosition += num2; sourcePosition += num2; continue; } throw new ZipException("Unexpected end of stream"); } } private void CopyEntryDataDirect(ZipUpdate update, Stream stream, bool updateCrc, ref long destinationPosition, ref long sourcePosition) { long num = update.Entry.CompressedSize; Crc32 crc = new Crc32(); byte[] buffer = GetBuffer(); long num2 = num; long num3 = 0L; int num5; do { int num4 = buffer.Length; if (num < num4) { num4 = (int)num; } stream.Position = sourcePosition; num5 = stream.Read(buffer, 0, num4); if (num5 > 0) { if (updateCrc) { crc.Update(new ArraySegment(buffer, 0, num5)); } stream.Position = destinationPosition; stream.Write(buffer, 0, num5); destinationPosition += num5; sourcePosition += num5; num -= num5; num3 += num5; } } while (num5 > 0 && num > 0); if (num3 != num2) { throw new ZipException($"Failed to copy bytes expected {num2} read {num3}"); } if (updateCrc) { update.OutEntry.Crc = crc.Value; } } private int FindExistingUpdate(ZipEntry entry) { int result = -1; if (updateIndex_.ContainsKey(entry.Name)) { result = updateIndex_[entry.Name]; } return result; } private int FindExistingUpdate(string fileName, bool isEntryName = false) { int result = -1; string text = ((!isEntryName) ? GetTransformedFileName(fileName) : fileName); if (updateIndex_.ContainsKey(text)) { result = updateIndex_[text]; } return result; } private Stream GetOutputStream(ZipEntry entry) { Stream stream = baseStream_; if (entry.IsCrypted) { stream = CreateAndInitEncryptionStream(stream, entry); } switch (entry.CompressionMethod) { case CompressionMethod.Stored: if (!entry.IsCrypted) { stream = new UncompressedStream(stream); } break; case CompressionMethod.Deflated: stream = new DeflaterOutputStream(stream, new Deflater(9, noZlibHeaderOrFooter: true)) { IsStreamOwner = entry.IsCrypted }; break; case CompressionMethod.BZip2: stream = new BZip2OutputStream(stream) { IsStreamOwner = entry.IsCrypted }; break; default: throw new ZipException("Unknown compression method " + entry.CompressionMethod); } return stream; } private void AddEntry(ZipFile workFile, ZipUpdate update) { Stream stream = null; if (update.Entry.IsFile) { stream = update.GetSource(); if (stream == null) { stream = updateDataSource_.GetSource(update.Entry, update.Filename); } } bool updateCrc = update.Entry.AESKeySize == 0; if (stream != null) { using (stream) { long length = stream.Length; if (update.OutEntry.Size < 0) { update.OutEntry.Size = length; } else if (update.OutEntry.Size != length) { throw new ZipException("Entry size/stream size mismatch"); } workFile.WriteLocalEntryHeader(update); long position = workFile.baseStream_.Position; using (Stream destination = workFile.GetOutputStream(update.OutEntry)) { CopyBytes(update, destination, stream, length, updateCrc); } long position2 = workFile.baseStream_.Position; update.OutEntry.CompressedSize = position2 - position; if ((update.OutEntry.Flags & 8) == 8) { ZipFormat.WriteDataDescriptor(workFile.baseStream_, update.OutEntry); } return; } } workFile.WriteLocalEntryHeader(update); update.OutEntry.CompressedSize = 0L; } private void ModifyEntry(ZipFile workFile, ZipUpdate update) { workFile.WriteLocalEntryHeader(update); long position = workFile.baseStream_.Position; if (update.Entry.IsFile && update.Filename != null) { using Stream destination = workFile.GetOutputStream(update.OutEntry); using Stream stream = GetInputStream(update.Entry); CopyBytes(update, destination, stream, stream.Length, updateCrc: true); } long position2 = workFile.baseStream_.Position; update.Entry.CompressedSize = position2 - position; } private void CopyEntryDirect(ZipFile workFile, ZipUpdate update, ref long destinationPosition) { bool num = update.Entry.Offset == destinationPosition; if (!num) { baseStream_.Position = destinationPosition; workFile.WriteLocalEntryHeader(update); destinationPosition = baseStream_.Position; } long num2 = 0L; long num3 = update.Entry.Offset + 26; baseStream_.Seek(num3, SeekOrigin.Begin); uint num4 = ReadLEUshort(); uint num5 = ReadLEUshort(); num2 = baseStream_.Position + num4 + num5; if (num) { if (update.OffsetBasedSize != -1) { destinationPosition += update.OffsetBasedSize; return; } destinationPosition += num2 - num3 + 26; destinationPosition += update.Entry.CompressedSize; baseStream_.Seek(destinationPosition, SeekOrigin.Begin); bool includingSignature = ReadLEUint() == 134695760; destinationPosition += GetDescriptorSize(update, includingSignature); } else { if (update.Entry.CompressedSize > 0) { CopyEntryDataDirect(update, baseStream_, updateCrc: false, ref destinationPosition, ref num2); } CopyDescriptorBytesDirect(update, baseStream_, ref destinationPosition, num2); } } private void CopyEntry(ZipFile workFile, ZipUpdate update) { workFile.WriteLocalEntryHeader(update); if (update.Entry.CompressedSize > 0) { long offset = update.Entry.Offset + 26; baseStream_.Seek(offset, SeekOrigin.Begin); uint num = ReadLEUshort(); uint num2 = ReadLEUshort(); baseStream_.Seek(num + num2, SeekOrigin.Current); CopyBytes(update, workFile.baseStream_, baseStream_, update.Entry.CompressedSize, updateCrc: 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 length = baseStream_.Length; Stream stream; if (archiveStorage_.UpdateMode == FileUpdateMode.Safe) { stream = archiveStorage_.MakeTemporaryCopy(baseStream_); baseStream_.Dispose(); baseStream_ = null; } else if (archiveStorage_.UpdateMode == FileUpdateMode.Direct) { baseStream_ = archiveStorage_.OpenForDirectUpdate(baseStream_); stream = baseStream_; } else { baseStream_.Dispose(); baseStream_ = null; stream = new FileStream(Name, FileMode.Open, FileAccess.ReadWrite); } try { if (ZipFormat.LocateBlockWithSignature(stream, 101010256, length, 22, 65535) < 0) { throw new ZipException("Cannot find central directory"); } stream.Position += 16L; byte[] rawComment = newComment_.RawComment; stream.WriteLEShort(rawComment.Length); stream.Write(rawComment, 0, rawComment.Length); stream.SetLength(stream.Position); } finally { if (stream != baseStream_) { stream.Dispose(); } } if (archiveStorage_.UpdateMode == FileUpdateMode.Safe) { Reopen(archiveStorage_.ConvertTemporaryToFinal()); } else { ReadEntries(); } } private void RunUpdates() { long num = 0L; long num2 = 0L; bool flag = false; long destinationPosition = 0L; ZipFile zipFile; if (IsNewArchive) { zipFile = this; zipFile.baseStream_.Position = 0L; flag = true; } else if (archiveStorage_.UpdateMode == FileUpdateMode.Direct) { zipFile = this; zipFile.baseStream_.Position = 0L; flag = true; updates_.Sort(new UpdateComparer()); } else { zipFile = Create(archiveStorage_.GetTemporaryOutput()); zipFile.UseZip64 = UseZip64; if (key != null) { zipFile.key = (byte[])key.Clone(); } } try { foreach (ZipUpdate item in updates_) { if (item == null) { continue; } switch (item.Command) { case UpdateCommand.Copy: if (flag) { CopyEntryDirect(zipFile, item, ref destinationPosition); } else { CopyEntry(zipFile, item); } break; case UpdateCommand.Modify: ModifyEntry(zipFile, item); break; case UpdateCommand.Add: if (!IsNewArchive && flag) { zipFile.baseStream_.Position = destinationPosition; } AddEntry(zipFile, item); if (flag) { destinationPosition = zipFile.baseStream_.Position; } break; } } if (!IsNewArchive && flag) { zipFile.baseStream_.Position = destinationPosition; } long position = zipFile.baseStream_.Position; foreach (ZipUpdate item2 in updates_) { if (item2 != null) { num += zipFile.WriteCentralDirectoryHeader(item2.OutEntry); } } byte[] comment = newComment_?.RawComment ?? _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment_); ZipFormat.WriteEndOfCentralDirectory(zipFile.baseStream_, updateCount_, num, position, comment); num2 = zipFile.baseStream_.Position; foreach (ZipUpdate item3 in updates_) { if (item3 == null) { continue; } if (item3.CrcPatchOffset > 0 && item3.OutEntry.CompressedSize > 0) { zipFile.baseStream_.Position = item3.CrcPatchOffset; zipFile.WriteLEInt((int)item3.OutEntry.Crc); } if (item3.SizePatchOffset > 0) { zipFile.baseStream_.Position = item3.SizePatchOffset; if (item3.OutEntry.LocalHeaderRequiresZip64) { zipFile.WriteLeLong(item3.OutEntry.Size); zipFile.WriteLeLong(item3.OutEntry.CompressedSize); } else { zipFile.WriteLEInt((int)item3.OutEntry.CompressedSize); zipFile.WriteLEInt((int)item3.OutEntry.Size); } } } } catch { zipFile.Close(); if (!flag && zipFile.Name != null) { File.Delete(zipFile.Name); } throw; } if (flag) { zipFile.baseStream_.SetLength(num2); zipFile.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"); } } void IDisposable.Dispose() { Close(); } private void DisposeInternal(bool disposing) { if (isDisposed_) { return; } isDisposed_ = true; entries_ = Empty.Array(); if (IsStreamOwner && baseStream_ != null) { lock (baseStream_) { baseStream_.Dispose(); } } PostUpdateCleanup(); } protected virtual void Dispose(bool disposing) { DisposeInternal(disposing); } private ushort ReadLEUshort() { int num = baseStream_.ReadByte(); if (num < 0) { throw new EndOfStreamException("End of stream"); } int num2 = baseStream_.ReadByte(); if (num2 < 0) { throw new EndOfStreamException("End of stream"); } return (ushort)((ushort)num | (ushort)(num2 << 8)); } private uint ReadLEUint() { return (uint)(ReadLEUshort() | (ReadLEUshort() << 16)); } private ulong ReadLEUlong() { return ReadLEUint() | ((ulong)ReadLEUint() << 32); } private long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData) { return ZipFormat.LocateBlockWithSignature(baseStream_, signature, endLocation, minimumBlockSize, maximumVariableData); } private void ReadEntries() { if (!baseStream_.CanSeek) { throw new ZipException("ZipFile stream must be seekable"); } long num = LocateBlockWithSignature(101010256, baseStream_.Length, 22, 65535); if (num < 0) { throw new ZipException("Cannot find central directory"); } ushort num2 = ReadLEUshort(); ushort num3 = ReadLEUshort(); ulong num4 = ReadLEUshort(); ulong num5 = ReadLEUshort(); ulong num6 = ReadLEUint(); long num7 = ReadLEUint(); uint num8 = ReadLEUshort(); if (num8 != 0) { byte[] array = new byte[num8]; StreamUtils.ReadFully(baseStream_, array); comment_ = _stringCodec.ZipArchiveCommentEncoding.GetString(array); } else { comment_ = string.Empty; } bool flag = false; bool flag2 = num2 == ushort.MaxValue || num3 == ushort.MaxValue || num4 == 65535 || num5 == 65535 || num6 == uint.MaxValue || num7 == uint.MaxValue; if (LocateBlockWithSignature(117853008, num - 4, 20, 0) < 0) { if (flag2) { throw new ZipException("Cannot find Zip64 locator"); } } else { flag = true; ReadLEUint(); ulong num9 = ReadLEUlong(); ReadLEUint(); baseStream_.Position = (long)num9; if ((ulong)ReadLEUint() != 101075792) { throw new ZipException($"Invalid Zip64 Central directory signature at {num9:X}"); } ReadLEUlong(); ReadLEUshort(); ReadLEUshort(); ReadLEUint(); ReadLEUint(); num4 = ReadLEUlong(); num5 = ReadLEUlong(); num6 = ReadLEUlong(); num7 = (long)ReadLEUlong(); } entries_ = new ZipEntry[num4]; if (!flag && num7 < num - (long)(4 + num6)) { offsetOfFirstEntry = num - ((long)(4 + num6) + num7); if (offsetOfFirstEntry <= 0) { throw new ZipException("Invalid embedded zip archive"); } } baseStream_.Seek(offsetOfFirstEntry + num7, SeekOrigin.Begin); for (ulong num10 = 0uL; num10 < num4; num10++) { if (ReadLEUint() != 33639248) { throw new ZipException("Wrong Central Directory signature"); } int madeByInfo = ReadLEUshort(); int versionRequiredToExtract = ReadLEUshort(); int flags = ReadLEUshort(); int method = ReadLEUshort(); uint num11 = ReadLEUint(); uint num12 = ReadLEUint(); long num13 = ReadLEUint(); long num14 = ReadLEUint(); int num15 = ReadLEUshort(); int num16 = ReadLEUshort(); int num17 = ReadLEUshort(); ReadLEUshort(); ReadLEUshort(); uint externalFileAttributes = ReadLEUint(); long offset = ReadLEUint(); byte[] array2 = new byte[Math.Max(num15, num17)]; Encoding encoding = _stringCodec.ZipInputEncoding(flags); StreamUtils.ReadFully(baseStream_, array2, 0, num15); string name = encoding.GetString(array2, 0, num15); bool unicode = encoding.IsZipUnicode(); ZipEntry zipEntry = new ZipEntry(name, versionRequiredToExtract, madeByInfo, (CompressionMethod)method, unicode) { Crc = ((long)num12 & 0xFFFFFFFFL), Size = (num14 & 0xFFFFFFFFu), CompressedSize = (num13 & 0xFFFFFFFFu), Flags = flags, DosTime = num11, ZipFileIndex = (long)num10, Offset = offset, ExternalFileAttributes = (int)externalFileAttributes }; if (!zipEntry.HasFlag(GeneralBitFlags.Descriptor)) { zipEntry.CryptoCheckValue = (byte)(num12 >> 24); } else { zipEntry.CryptoCheckValue = (byte)((num11 >> 8) & 0xFF); } if (num16 > 0) { byte[] array3 = new byte[num16]; StreamUtils.ReadFully(baseStream_, array3); zipEntry.ExtraData = array3; } zipEntry.ProcessExtraData(localHeader: false); if (num17 > 0) { StreamUtils.ReadFully(baseStream_, array2, 0, num17); zipEntry.Comment = encoding.GetString(array2, 0, num17); } entries_[num10] = zipEntry; } } private long LocateEntry(ZipEntry entry) { return TestLocalHeader(entry, (!SkipLocalEntryTestsOnLocate) ? HeaderTest.Extract : HeaderTest.None); } private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry) { CryptoStream cryptoStream = null; if (entry.CompressionMethodForHeader == CompressionMethod.WinZipAES) { if (entry.Version < 51) { throw new ZipException("Decryption method not supported"); } OnKeysRequired(entry.Name); if (rawPassword_ == null) { throw new ZipException("No password available for AES encrypted stream"); } int aESSaltLen = entry.AESSaltLen; byte[] array = new byte[aESSaltLen]; int num = StreamUtils.ReadRequestedBytes(baseStream, array, 0, aESSaltLen); if (num != aESSaltLen) { throw new ZipException($"AES Salt expected {aESSaltLen} git {num}"); } byte[] array2 = new byte[2]; StreamUtils.ReadFully(baseStream, array2); int blockSize = entry.AESKeySize / 8; ZipAESTransform zipAESTransform = new ZipAESTransform(rawPassword_, array, blockSize, writeMode: false); byte[] pwdVerifier = zipAESTransform.PwdVerifier; if (pwdVerifier[0] != array2[0] || pwdVerifier[1] != array2[1]) { throw new ZipException("Invalid password for AES"); } cryptoStream = new ZipAESStream(baseStream, zipAESTransform, CryptoStreamMode.Read); } else { if (entry.Version >= 50 && entry.HasFlag(GeneralBitFlags.StrongEncryption)) { throw new ZipException("Decryption method not supported"); } PkzipClassicManaged pkzipClassicManaged = new PkzipClassicManaged(); OnKeysRequired(entry.Name); if (!HaveKeys) { throw new ZipException("No password available for encrypted stream"); } cryptoStream = new CryptoStream(baseStream, pkzipClassicManaged.CreateDecryptor(key, null), CryptoStreamMode.Read); CheckClassicPassword(cryptoStream, entry); } return cryptoStream; } private Stream CreateAndInitEncryptionStream(Stream baseStream, ZipEntry entry) { if (entry.Version >= 50 && entry.HasFlag(GeneralBitFlags.StrongEncryption)) { return null; } PkzipClassicManaged pkzipClassicManaged = new PkzipClassicManaged(); OnKeysRequired(entry.Name); if (!HaveKeys) { throw new ZipException("No password available for encrypted stream"); } CryptoStream cryptoStream = new CryptoStream(new UncompressedStream(baseStream), pkzipClassicManaged.CreateEncryptor(key, null), CryptoStreamMode.Write); if (entry.Crc < 0 || entry.HasFlag(GeneralBitFlags.Descriptor)) { WriteEncryptionHeader(cryptoStream, entry.DosTime << 16); } else { WriteEncryptionHeader(cryptoStream, entry.Crc); } return cryptoStream; } private static void CheckClassicPassword(CryptoStream classicCryptoStream, ZipEntry entry) { byte[] array = new byte[12]; StreamUtils.ReadFully(classicCryptoStream, array); if (array[11] != entry.CryptoCheckValue) { throw new ZipException("Invalid password"); } } private static void WriteEncryptionHeader(Stream stream, long crcValue) { byte[] array = new byte[12]; using (RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create()) { randomNumberGenerator.GetBytes(array); } array[11] = (byte)(crcValue >> 24); stream.Write(array, 0, array.Length); } } public interface IStaticDataSource { Stream GetSource(); } public interface IDynamicDataSource { Stream GetSource(ZipEntry entry, string name); } public class StaticDiskDataSource : IStaticDataSource { private readonly string fileName_; public StaticDiskDataSource(string fileName) { fileName_ = fileName; } public Stream GetSource() { return File.Open(fileName_, FileMode.Open, FileAccess.Read, FileShare.Read); } } public class DynamicDiskDataSource : IDynamicDataSource { 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; } } public interface IArchiveStorage { FileUpdateMode UpdateMode { get; } Stream GetTemporaryOutput(); Stream ConvertTemporaryToFinal(); Stream MakeTemporaryCopy(Stream stream); Stream OpenForDirectUpdate(Stream stream); void Dispose(); } public abstract class BaseArchiveStorage : IArchiveStorage { private readonly FileUpdateMode updateMode_; public FileUpdateMode UpdateMode => updateMode_; protected BaseArchiveStorage(FileUpdateMode updateMode) { updateMode_ = updateMode; } public abstract Stream GetTemporaryOutput(); public abstract Stream ConvertTemporaryToFinal(); public abstract Stream MakeTemporaryCopy(Stream stream); public abstract Stream OpenForDirectUpdate(Stream stream); public abstract void Dispose(); } public class DiskArchiveStorage : BaseArchiveStorage { private Stream temporaryStream_; private readonly string fileName_; private string temporaryName_; public DiskArchiveStorage(ZipFile file, FileUpdateMode updateMode) : base(updateMode) { if (file.Name == null) { throw new ZipException("Cant handle non file archives"); } fileName_ = file.Name; } public DiskArchiveStorage(ZipFile file) : this(file, FileUpdateMode.Safe) { } public override Stream GetTemporaryOutput() { temporaryName_ = PathUtils.GetTempFileName(temporaryName_); temporaryStream_ = File.Open(temporaryName_, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); return temporaryStream_; } public override Stream ConvertTemporaryToFinal() { if (temporaryStream_ == null) { throw new ZipException("No temporary stream has been created"); } Stream stream = null; string tempFileName = PathUtils.GetTempFileName(fileName_); bool flag = false; try { temporaryStream_.Dispose(); File.Move(fileName_, tempFileName); File.Move(temporaryName_, fileName_); flag = true; File.Delete(tempFileName); return File.Open(fileName_, FileMode.Open, FileAccess.Read, FileShare.Read); } catch (Exception) { stream = null; if (!flag) { File.Move(tempFileName, fileName_); File.Delete(temporaryName_); } throw; } } public override Stream MakeTemporaryCopy(Stream stream) { stream.Dispose(); temporaryName_ = PathUtils.GetTempFileName(fileName_); File.Copy(fileName_, temporaryName_, overwrite: true); temporaryStream_ = new FileStream(temporaryName_, FileMode.Open, FileAccess.ReadWrite); return temporaryStream_; } public override Stream OpenForDirectUpdate(Stream stream) { if (stream == null || !stream.CanWrite) { stream?.Dispose(); return new FileStream(fileName_, FileMode.Open, FileAccess.ReadWrite); } return stream; } public override void Dispose() { if (temporaryStream_ != null) { temporaryStream_.Dispose(); } } } public class MemoryArchiveStorage : BaseArchiveStorage { private MemoryStream temporaryStream_; private MemoryStream finalStream_; public MemoryStream FinalStream => finalStream_; public MemoryArchiveStorage() : base(FileUpdateMode.Direct) { } public MemoryArchiveStorage(FileUpdateMode updateMode) : base(updateMode) { } public override Stream GetTemporaryOutput() { temporaryStream_ = new MemoryStream(); return temporaryStream_; } public override Stream ConvertTemporaryToFinal() { if (temporaryStream_ == null) { throw new ZipException("No temporary stream has been created"); } finalStream_ = new MemoryStream(temporaryStream_.ToArray()); return finalStream_; } public override Stream MakeTemporaryCopy(Stream stream) { temporaryStream_ = new MemoryStream(); stream.Position = 0L; StreamUtils.Copy(stream, temporaryStream_, new byte[4096]); return temporaryStream_; } public override Stream OpenForDirectUpdate(Stream stream) { Stream stream2; if (stream == null || !stream.CanWrite) { stream2 = new MemoryStream(); if (stream != null) { stream.Position = 0L; StreamUtils.Copy(stream, stream2, new byte[4096]); stream.Dispose(); } } else { stream2 = stream; } return stream2; } public override void Dispose() { if (temporaryStream_ != null) { temporaryStream_.Dispose(); } } } public class DescriptorData { private long _crc; public long CompressedSize { get; set; } public long Size { get; set; } public long Crc { get { return _crc; } set { _crc = value & 0xFFFFFFFFu; } } } internal struct EntryPatchData { public long SizePatchOffset { get; set; } public long CrcPatchOffset { get; set; } } internal static class ZipFormat { internal static int WriteLocalHeader(Stream stream, ZipEntry entry, out EntryPatchData patchData, bool headerInfoAvailable, bool patchEntryHeader, long streamOffset, StringCodec stringCodec) { patchData = default(EntryPatchData); stream.WriteLEInt(67324752); 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); if (patchEntryHeader) { patchData.SizePatchOffset = streamOffset + stream.Position; } if (entry.LocalHeaderRequiresZip64 && patchEntryHeader) { stream.WriteLEInt(-1); stream.WriteLEInt(-1); } else { stream.WriteLEInt(0); stream.WriteLEInt(0); } } byte[] bytes = stringCodec.ZipOutputEncoding.GetBytes(entry.Name); if (bytes.Length > 65535) { throw new ZipException("Entry name too long."); } ZipExtraData zipExtraData = new ZipExtraData(entry.ExtraData); if (entry.LocalHeaderRequiresZip64) { zipExtraData.StartNewEntry(); if (headerInfoAvailable) { zipExtraData.AddLeLong(entry.Size); zipExtraData.AddLeLong(entry.CompressedSize + entry.EncryptionOverheadSize); } else { zipExtraData.AddLeLong(0L); zipExtraData.AddLeLong(0L); } zipExtraData.AddNewEntry(1); if (!zipExtraData.Find(1)) { throw new ZipException("Internal error cant find extra data"); } patchData.SizePatchOffset = zipExtraData.CurrentReadIndex; } else { zipExtraData.Delete(1); } if (entry.AESKeySize > 0) { AddExtraDataAES(entry, zipExtraData); } byte[] entryData = zipExtraData.GetEntryData(); stream.WriteLEShort(bytes.Length); stream.WriteLEShort(entryData.Length); if (bytes.Length != 0) { stream.Write(bytes, 0, bytes.Length); } if (entry.LocalHeaderRequiresZip64 && patchEntryHeader) { patchData.SizePatchOffset += streamOffset + stream.Position; } if (entryData.Length != 0) { stream.Write(entryData, 0, entryData.Length); } return 30 + bytes.Length + entryData.Length; } internal static long LocateBlockWithSignature(Stream stream, int signature, long endLocation, int minimumBlockSize, int maximumVariableData) { long num = endLocation - minimumBlockSize; if (num < 0) { return -1L; } long num2 = Math.Max(num - maximumVariableData, 0L); do { if (num < num2) { return -1L; } stream.Seek(num--, 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(delegate(Stream s) { WriteZip64EndOfCentralDirectory(s, noOfEntries, sizeEntries, centralDirOffset); }, cancellationToken); } internal static void WriteZip64EndOfCentralDirectory(Stream stream, long noOfEntries, long sizeEntries, long centralDirOffset) { long value = centralDirOffset + sizeEntries; stream.WriteLEInt(101075792); stream.WriteLELong(44L); stream.WriteLEShort(51); stream.WriteLEShort(45); stream.WriteLEInt(0); stream.WriteLEInt(0); stream.WriteLELong(noOfEntries); stream.WriteLELong(noOfEntries); stream.WriteLELong(sizeEntries); stream.WriteLELong(centralDirOffset); stream.WriteLEInt(117853008); stream.WriteLEInt(0); stream.WriteLELong(value); stream.WriteLEInt(1); } public static async Task WriteEndOfCentralDirectoryAsync(Stream stream, long noOfEntries, long sizeEntries, long start, byte[] comment, CancellationToken cancellationToken) { await stream.WriteProcToStreamAsync(delegate(Stream s) { WriteEndOfCentralDirectory(s, noOfEntries, sizeEntries, start, comment); }, cancellationToken); } internal static void WriteEndOfCentralDirectory(Stream stream, long noOfEntries, long sizeEntries, long start, byte[] comment) { if (noOfEntries >= 65535 || start >= uint.MaxValue || sizeEntries >= uint.MaxValue) { WriteZip64EndOfCentralDirectory(stream, noOfEntries, sizeEntries, start); } stream.WriteLEInt(101010256); stream.WriteLEShort(0); stream.WriteLEShort(0); if (noOfEntries >= 65535) { stream.WriteLEUshort(ushort.MaxValue); stream.WriteLEUshort(ushort.MaxValue); } else { stream.WriteLEShort((short)noOfEntries); stream.WriteLEShort((short)noOfEntries); } if (sizeEntries >= uint.MaxValue) { stream.WriteLEUint(uint.MaxValue); } else { stream.WriteLEInt((int)sizeEntries); } if (start >= uint.MaxValue) { stream.WriteLEUint(uint.MaxValue); } else { stream.WriteLEInt((int)start); } int num = ((comment != null) ? comment.Length : 0); if (num > 65535) { throw new ZipException($"Comment length ({num}) is larger than 64K"); } stream.WriteLEShort(num); if (num > 0) { stream.Write(comment, 0, num); } } internal static int WriteDataDescriptor(Stream stream, ZipEntry entry) { if (entry == null) { throw new ArgumentNullException("entry"); } int num = 0; if ((entry.Flags & 8) != 0) { stream.WriteLEInt(134695760); stream.WriteLEInt((int)entry.Crc); num += 8; if (entry.LocalHeaderRequiresZip64) { stream.WriteLELong(entry.CompressedSize); stream.WriteLELong(entry.Size); num += 16; } else { stream.WriteLEInt((int)entry.CompressedSize); stream.WriteLEInt((int)entry.Size); num += 8; } } return num; } internal static void ReadDataDescriptor(Stream stream, bool zip64, DescriptorData data) { if (stream.ReadLEInt() != 134695760) { 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(33639248); 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[] bytes = stringCodec.ZipOutputEncoding.GetBytes(entry.Name); if (bytes.Length > 65535) { throw new ZipException("Name too long."); } ZipExtraData zipExtraData = new ZipExtraData(entry.ExtraData); if (entry.CentralHeaderRequiresZip64) { zipExtraData.StartNewEntry(); if (entry.IsZip64Forced() || entry.Size >= uint.MaxValue) { zipExtraData.AddLeLong(entry.Size); } if (entry.IsZip64Forced() || entry.CompressedSize >= uint.MaxValue) { zipExtraData.AddLeLong(entry.CompressedSize); } if (entry.Offset >= uint.MaxValue) { zipExtraData.AddLeLong(entry.Offset); } zipExtraData.AddNewEntry(1); } else { zipExtraData.Delete(1); } if (entry.AESKeySize > 0) { AddExtraDataAES(entry, zipExtraData); } byte[] entryData = zipExtraData.GetEntryData(); byte[] array = ((entry.Comment != null) ? stringCodec.ZipOutputEncoding.GetBytes(entry.Comment) : Empty.Array()); if (array.Length > 65535) { throw new ZipException("Comment too long."); } stream.WriteLEShort(bytes.Length); stream.WriteLEShort(entryData.Length); stream.WriteLEShort(array.Length); stream.WriteLEShort(0); stream.WriteLEShort(0); if (entry.ExternalFileAttributes != -1) { stream.WriteLEInt(entry.ExternalFileAttributes); } else if (entry.IsDirectory) { stream.WriteLEInt(16); } else { stream.WriteLEInt(0); } if (entry.Offset >= uint.MaxValue) { stream.WriteLEInt(-1); } else { stream.WriteLEInt((int)entry.Offset); } if (bytes.Length != 0) { stream.Write(bytes, 0, bytes.Length); } if (entryData.Length != 0) { stream.Write(entryData, 0, entryData.Length); } if (array.Length != 0) { stream.Write(array, 0, array.Length); } return 46 + bytes.Length + entryData.Length + array.Length; } internal static void AddExtraDataAES(ZipEntry entry, ZipExtraData extraData) { extraData.StartNewEntry(); extraData.AddLeShort(2); extraData.AddLeShort(17729); extraData.AddData(entry.AESEncryptionStrength); extraData.AddLeShort((int)entry.CompressionMethod); extraData.AddNewEntry(39169); } internal static async Task PatchLocalHeaderAsync(Stream stream, ZipEntry entry, EntryPatchData patchData, CancellationToken ct) { long initialPos = stream.Position; stream.Seek(patchData.CrcPatchOffset, SeekOrigin.Begin); await stream.WriteLEIntAsync((int)entry.Crc, ct); if (!entry.LocalHeaderRequiresZip64) { await stream.WriteLEIntAsync((int)entry.CompressedSize, ct); await stream.WriteLEIntAsync((int)entry.Size, ct); } else { if (patchData.SizePatchOffset == -1) { throw new ZipException("Entry requires zip64 but this has been turned off"); } stream.Seek(patchData.SizePatchOffset, SeekOrigin.Begin); await stream.WriteLELongAsync(entry.Size, ct); await stream.WriteLELongAsync(entry.CompressedSize, ct); } stream.Seek(initialPos, SeekOrigin.Begin); } internal static void PatchLocalHeaderSync(Stream stream, ZipEntry entry, EntryPatchData patchData) { long position = 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"); } stream.Seek(patchData.SizePatchOffset, SeekOrigin.Begin); stream.WriteLELong(entry.Size); stream.WriteLELong(entry.CompressedSize); } else { stream.WriteLEInt((int)entry.CompressedSize); stream.WriteLEInt((int)entry.Size); } stream.Seek(position, SeekOrigin.Begin); } } public class ZipInputStream : InflaterInputStream { private delegate int ReadDataHandler(byte[] b, int offset, int length); 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(); public string Password { get { return password; } set { password = value; } } public bool CanDecompressEntry { get { if (entry != null && IsEntryCompressionMethodSupported(entry) && entry.CanDecompress) { if (entry.HasFlag(GeneralBitFlags.Descriptor) && entry.CompressionMethod == CompressionMethod.Stored) { return entry.IsCrypted; } return true; } return false; } } public override int Available { get { if (entry == null) { return 0; } return 1; } } public override long Length { get { if (entry != null) { if (entry.Size >= 0) { return entry.Size; } throw new ZipException("Length not available for the current entry"); } throw new InvalidOperationException("No current entry"); } } public ZipInputStream(Stream baseInputStream) : base(baseInputStream, new Inflater(noHeader: true)) { internalReader = ReadingNotAvailable; } public ZipInputStream(Stream baseInputStream, int bufferSize) : base(baseInputStream, new Inflater(noHeader: true), bufferSize) { internalReader = ReadingNotAvailable; } private static bool IsEntryCompressionMethodSupported(ZipEntry entry) { CompressionMethod compressionMethodForHeader = entry.CompressionMethodForHeader; if (compressionMethodForHeader != CompressionMethod.Deflated) { return compressionMethodForHeader == CompressionMethod.Stored; } return true; } public ZipEntry GetNextEntry() { if (crc == null) { throw new InvalidOperationException("Closed."); } if (entry != null) { CloseEntry(); } if (!SkipUntilNextEntry()) { Dispose(); return null; } short versionRequiredToExtract = (short)inputBuffer.ReadLeShort(); flags = inputBuffer.ReadLeShort(); method = (CompressionMethod)inputBuffer.ReadLeShort(); uint num = (uint)inputBuffer.ReadLeInt(); int num2 = inputBuffer.ReadLeInt(); csize = inputBuffer.ReadLeInt(); size = inputBuffer.ReadLeInt(); int num3 = inputBuffer.ReadLeShort(); int num4 = inputBuffer.ReadLeShort(); bool flag = (flags & 1) == 1; byte[] array = new byte[num3]; inputBuffer.ReadRawBuffer(array); Encoding encoding = _stringCodec.ZipInputEncoding(flags); string name = encoding.GetString(array); bool unicode = encoding.IsZipUnicode(); entry = new ZipEntry(name, versionRequiredToExtract, 51, method, unicode) { Flags = flags }; if ((flags & 8) == 0) { entry.Crc = num2 & 0xFFFFFFFFu; entry.Size = size & 0xFFFFFFFFu; entry.CompressedSize = csize & 0xFFFFFFFFu; entry.CryptoCheckValue = (byte)((num2 >> 24) & 0xFF); } else { if (num2 != 0) { entry.Crc = num2 & 0xFFFFFFFFu; } if (size != 0L) { entry.Size = size & 0xFFFFFFFFu; } if (csize != 0L) { entry.CompressedSize = csize & 0xFFFFFFFFu; } entry.CryptoCheckValue = (byte)((num >> 8) & 0xFF); } entry.DosTime = num; if (num4 > 0) { byte[] array2 = new byte[num4]; inputBuffer.ReadRawBuffer(array2); entry.ExtraData = array2; } entry.ProcessExtraData(localHeader: true); if (entry.CompressedSize >= 0) { csize = entry.CompressedSize; } if (entry.Size >= 0) { size = entry.Size; } if (method == CompressionMethod.Stored && ((!flag && csize != size) || (flag && csize - 12 != size))) { throw new ZipException("Stored, but compressed != uncompressed"); } if (IsEntryCompressionMethodSupported(entry)) { internalReader = InitialRead; } else { internalReader = ReadingNotSupported; } return entry; } private bool SkipUntilNextEntry() { int num = 0; while (inputBuffer.ReadLeByte() == 0) { num++; } inputBuffer.Available++; _ = 0; int num2 = 0; uint num3 = (uint)inputBuffer.ReadLeInt(); while (true) { switch (num3) { case 33639248u: case 84233040u: case 101010256u: case 101075792u: case 117853008u: return false; case 67324752u: return true; } num3 = (uint)(inputBuffer.ReadLeByte() << 24) | (num3 >> 8); num2++; } } private void ReadDataDescriptor() { if (inputBuffer.ReadLeInt() != 134695760) { throw new ZipException("Data descriptor signature not found"); } entry.Crc = inputBuffer.ReadLeInt() & 0xFFFFFFFFu; if (entry.LocalHeaderRequiresZip64) { csize = inputBuffer.ReadLeLong(); size = inputBuffer.ReadLeLong(); } else { csize = inputBuffer.ReadLeInt(); size = inputBuffer.ReadLeInt(); } entry.CompressedSize = csize; entry.Size = size; } private void CompleteCloseEntry(bool testCrc) { StopDecrypting(); if ((flags & 8) != 0) { ReadDataDescriptor(); } size = 0L; if (testCrc && (crc.Value & 0xFFFFFFFFu) != entry.Crc && entry.Crc != -1) { throw new ZipException("CRC mismatch"); } crc.Reset(); if (method == CompressionMethod.Deflated) { inf.Reset(); } entry = null; } public void CloseEntry() { if (crc == null) { throw new InvalidOperationException("Closed"); } if (entry == null) { return; } if (method == CompressionMethod.Deflated) { if ((flags & 8) != 0) { byte[] array = new byte[4096]; while (Read(array, 0, array.Length) > 0) { } return; } csize -= inf.TotalIn; inputBuffer.Available += inf.RemainingInput; } if (inputBuffer.Available > csize && csize >= 0) { inputBuffer.Available = (int)(inputBuffer.Available - csize); } else { csize -= inputBuffer.Available; inputBuffer.Available = 0; while (csize != 0L) { long num = Skip(csize); if (num <= 0) { throw new ZipException("Zip archive ends early."); } csize -= num; } } CompleteCloseEntry(testCrc: false); } public override int ReadByte() { byte[] array = new byte[1]; if (Read(array, 0, 1) <= 0) { return -1; } return array[0] & 0xFF; } private int ReadingNotAvailable(byte[] destination, int offset, int count) { throw new InvalidOperationException("Unable to read from this stream"); } private int ReadingNotSupported(byte[] destination, int offset, int count) { throw new ZipException("The compression method for this entry is not supported"); } 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"); } private int InitialRead(byte[] destination, int offset, int count) { bool flag = (entry.Flags & 8) != 0; if (entry.IsCrypted) { if (password == null) { throw new ZipException("No password set."); } PkzipClassicManaged pkzipClassicManaged = new PkzipClassicManaged(); byte[] rgbKey = PkzipClassic.GenerateKeys(_stringCodec.ZipCryptoEncoding.GetBytes(password)); inputBuffer.CryptoTransform = pkzipClassicManaged.CreateDecryptor(rgbKey, null); byte[] array = new byte[12]; inputBuffer.ReadClearTextBuffer(array, 0, 12); if (array[11] != entry.CryptoCheckValue) { throw new ZipException("Invalid password"); } if (csize >= 12) { csize -= 12L; } else if (!flag) { throw new ZipException($"Entry compressed size {csize} too small for encryption"); } } else { inputBuffer.CryptoTransform = null; } if (csize > 0 || flag) { if (method == CompressionMethod.Deflated && inputBuffer.Available > 0) { inputBuffer.SetInflaterInput(inf); } if (!entry.IsCrypted && method == CompressionMethod.Stored && flag) { 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; } public override int Read(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "Cannot be negative"); } if (count < 0) { throw new ArgumentOutOfRangeException("count", "Cannot be negative"); } if (buffer.Length - offset < count) { throw new ArgumentException("Invalid offset/count combination"); } return internalReader(buffer, offset, count); } 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 flag = 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; if ((flags & 8) == 0 && ((inf.TotalIn != csize && csize != uint.MaxValue && csize != -1) || inf.TotalOut != size)) { throw new ZipException("Size mismatch: " + csize + ";" + size + " <-> " + inf.TotalIn + ";" + inf.TotalOut); } inf.Reset(); flag = 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 == 0L) { flag = true; } else if (count < 0) { throw new ZipException("EOF in stored block"); } break; } if (count > 0) { crc.Update(new ArraySegment(buffer, offset, count)); } if (flag) { CompleteCloseEntry(testCrc: true); } return count; } protected override void Dispose(bool disposing) { internalReader = ReadingNotAvailable; crc = null; entry = null; base.Dispose(disposing); } } public class ZipNameTransform : INameTransform { private string trimPrefix_; private static readonly char[] InvalidEntryChars; private static readonly char[] InvalidEntryCharsRelaxed; public string TrimPrefix { get { return trimPrefix_; } set { trimPrefix_ = value; if (trimPrefix_ != null) { trimPrefix_ = trimPrefix_.ToLower(); } } } public ZipNameTransform() { } public ZipNameTransform(string trimPrefix) { TrimPrefix = trimPrefix; } static ZipNameTransform() { char[] invalidPathChars = Path.GetInvalidPathChars(); int num = invalidPathChars.Length + 2; InvalidEntryCharsRelaxed = new char[num]; Array.Copy(invalidPathChars, 0, InvalidEntryCharsRelaxed, 0, invalidPathChars.Length); InvalidEntryCharsRelaxed[num - 1] = '*'; InvalidEntryCharsRelaxed[num - 2] = '?'; num = invalidPathChars.Length + 4; InvalidEntryChars = new char[num]; Array.Copy(invalidPathChars, 0, InvalidEntryChars, 0, invalidPathChars.Length); InvalidEntryChars[num - 1] = ':'; InvalidEntryChars[num - 2] = '\\'; InvalidEntryChars[num - 3] = '*'; InvalidEntryChars[num - 4] = '?'; } public string TransformDirectory(string name) { name = TransformFile(name); if (name.Length > 0) { if (!name.EndsWith("/", StringComparison.Ordinal)) { name += "/"; } return name; } throw new ZipException("Cannot have an empty directory name"); } public string TransformFile(string name) { if (name != null) { string text = name.ToLower(); if (trimPrefix_ != null && text.IndexOf(trimPrefix_, StringComparison.Ordinal) == 0) { name = name.Substring(trimPrefix_.Length); } name = name.Replace("\\", "/"); name = PathUtils.DropPathRoot(name); name = name.Trim(new char[1] { '/' }); for (int num = name.IndexOf("//", StringComparison.Ordinal); num >= 0; num = name.IndexOf("//", StringComparison.Ordinal)) { name = name.Remove(num, 1); } name = MakeValidName(name, '_'); } else { name = string.Empty; } return name; } private static string MakeValidName(string name, char replacement) { int num = name.IndexOfAny(InvalidEntryChars); if (num >= 0) { StringBuilder stringBuilder = new StringBuilder(name); while (num >= 0) { stringBuilder[num] = replacement; num = ((num < name.Length) ? name.IndexOfAny(InvalidEntryChars, num + 1) : (-1)); } name = stringBuilder.ToString(); } if (name.Length > 65535) { throw new PathTooLongException(); } return name; } public static bool IsValidName(string name, bool relaxed) { bool flag = name != null; if (flag) { flag = ((!relaxed) ? (name.IndexOfAny(InvalidEntryChars) < 0 && name.IndexOf('/') != 0) : (name.IndexOfAny(InvalidEntryCharsRelaxed) < 0)); } return flag; } public static bool IsValidName(string name) { if (name != null && name.IndexOfAny(InvalidEntryChars) < 0) { return name.IndexOf('/') != 0; } return false; } } public class PathTransformer : INameTransform { public string TransformDirectory(string name) { name = TransformFile(name); if (name.Length > 0) { if (!name.EndsWith("/", StringComparison.Ordinal)) { name += "/"; } return name; } throw new ZipException("Cannot have an empty directory name"); } public string TransformFile(string name) { if (name != null) { name = name.Replace("\\", "/"); name = PathUtils.DropPathRoot(name); name = name.Trim(new char[1] { '/' }); for (int num = name.IndexOf("//", StringComparison.Ordinal); num >= 0; num = name.IndexOf("//", StringComparison.Ordinal)) { name = name.Remove(num, 1); } } else { name = string.Empty; } return name; } } public class ZipOutputStream : DeflaterOutputStream { private List entries = new List(); private Crc32 crc = new Crc32(); private ZipEntry curEntry; private bool entryIsPassthrough; private int defaultCompressionLevel = -1; private CompressionMethod curMethod = CompressionMethod.Deflated; private long size; private long offset; private byte[] zipComment = Empty.Array(); private bool patchEntryHeader; private EntryPatchData patchData; private UseZip64 useZip64_ = UseZip64.Dynamic; private string password; private readonly StringCodec _stringCodec = ZipStrings.GetStringCodec(); private static RandomNumberGenerator _aesRnd = RandomNumberGenerator.Create(); public bool IsFinished => entries == null; public UseZip64 UseZip64 { get { return useZip64_; } set { useZip64_ = value; } } public INameTransform NameTransform { get; set; } = new PathTransformer(); public string Password { get { return password; } set { if (value != null && value.Length == 0) { password = null; } else { password = value; } } } public ZipOutputStream(Stream baseOutputStream) : base(baseOutputStream, new Deflater(-1, noZlibHeaderOrFooter: true)) { } public ZipOutputStream(Stream baseOutputStream, int bufferSize) : base(baseOutputStream, new Deflater(-1, noZlibHeaderOrFooter: true), bufferSize) { } internal ZipOutputStream(Stream baseOutputStream, StringCodec stringCodec) : this(baseOutputStream) { _stringCodec = stringCodec; } public void SetComment(string comment) { byte[] bytes = _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment); if (bytes.Length > 65535) { throw new ArgumentOutOfRangeException("comment"); } zipComment = bytes; } public void SetLevel(int level) { deflater_.SetLevel(level); defaultCompressionLevel = level; } public int GetLevel() { return deflater_.GetLevel(); } private void WriteLeShort(int value) { baseOutputStream_.WriteByte((byte)(value & 0xFF)); baseOutputStream_.WriteByte((byte)((value >> 8) & 0xFF)); } private void WriteLeInt(int value) { WriteLeShort(value); WriteLeShort(value >> 16); } private void WriteLeLong(long value) { WriteLeInt((int)value); WriteLeInt((int)(value >> 32)); } private void TransformEntryName(ZipEntry entry) { if (NameTransform != null) { entry.Name = (entry.IsDirectory ? NameTransform.TransformDirectory(entry.Name) : NameTransform.TransformFile(entry.Name)); } } public void PutNextEntry(ZipEntry entry) { if (curEntry != null) { CloseEntry(); } PutNextEntry(baseOutputStream_, entry, 0L); if (entry.IsCrypted) { WriteOutput(GetEntryEncryptionHeader(entry)); } } 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, 0L, passthroughEntry: true); } private void WriteOutput(byte[] bytes) { baseOutputStream_.Write(bytes, 0, bytes.Length); } private Task WriteOutputAsync(byte[] bytes) { return baseOutputStream_.WriteAsync(bytes, 0, bytes.Length); } private byte[] GetEntryEncryptionHeader(ZipEntry entry) { if (entry.AESKeySize <= 0) { return CreateZipCryptoHeader((entry.Crc < 0) ? (entry.DosTime << 16) : entry.Crc); } return InitializeAESPassword(entry, Password); } internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0L, bool passthroughEntry = false) { if (entry == null) { throw new ArgumentNullException("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 compressionMethod = entry.CompressionMethod; if (compressionMethod != CompressionMethod.Deflated && compressionMethod != CompressionMethod.Stored) { throw new NotImplementedException("Compression method not supported"); } if (entry.AESKeySize > 0 && string.IsNullOrEmpty(Password)) { throw new InvalidOperationException("The Password property must be set before AES encrypted entries can be added"); } entryIsPassthrough = passthroughEntry; int level = defaultCompressionLevel; entry.Flags &= 2048; patchEntryHeader = false; bool flag; if (entry.Size == 0L && !entryIsPassthrough) { entry.CompressedSize = entry.Size; entry.Crc = 0L; compressionMethod = CompressionMethod.Stored; flag = true; } else { flag = entry.Size >= 0 && entry.HasCrc && entry.CompressedSize >= 0; if (compressionMethod == CompressionMethod.Stored) { if (!flag) { if (!base.CanPatchEntries) { compressionMethod = CompressionMethod.Deflated; level = 0; } } else { entry.CompressedSize = entry.Size; flag = entry.HasCrc; } } } if (!flag) { if (!base.CanPatchEntries) { entry.Flags |= 8; } else { patchEntryHeader = true; } } if (Password != null) { entry.IsCrypted = true; if (entry.Crc < 0) { entry.Flags |= 8; } } entry.Offset = offset; entry.CompressionMethod = compressionMethod; curMethod = compressionMethod; if (useZip64_ == UseZip64.On || (entry.Size < 0 && useZip64_ == UseZip64.Dynamic)) { entry.ForceZip64(); } TransformEntryName(entry); offset += ZipFormat.WriteLocalHeader(stream, entry, out var entryPatchData, flag, patchEntryHeader, streamOffset, _stringCodec); patchData = entryPatchData; if (entry.AESKeySize > 0) { offset += entry.AESOverheadSize; } curEntry = entry; size = 0L; if (!entryIsPassthrough) { crc.Reset(); if (compressionMethod == CompressionMethod.Deflated) { deflater_.Reset(); deflater_.SetLevel(level); } } } public async Task PutNextEntryAsync(ZipEntry entry, CancellationToken ct = default(CancellationToken)) { if (curEntry != null) { await CloseEntryAsync(ct); } long position = (base.CanPatchEntries ? baseOutputStream_.Position : (-1)); await baseOutputStream_.WriteProcToStreamAsync(delegate(Stream s) { PutNextEntry(s, entry, position); }, ct); if (entry.IsCrypted) { await WriteOutputAsync(GetEntryEncryptionHeader(entry)); } } public void CloseEntry() { WriteEntryFooter(baseOutputStream_); 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); 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 totalOut = size; if (curMethod == CompressionMethod.Deflated) { if (size >= 0) { base.Finish(); totalOut = deflater_.TotalOut; } else { deflater_.Reset(); } } else if (curMethod == CompressionMethod.Stored) { GetAuthCodeIfAES(); } if (curEntry.AESKeySize > 0) { stream.Write(AESAuthCode, 0, 10); curEntry.Crc = 0L; } 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 = totalOut; } else if (curEntry.CompressedSize != totalOut) { throw new ZipException($"compressed size was {totalOut}, but {curEntry.CompressedSize} expected"); } offset += totalOut; if (curEntry.IsCrypted) { curEntry.CompressedSize += curEntry.EncryptionOverheadSize; } if ((curEntry.Flags & 8) != 0) { stream.WriteLEInt(134695760); stream.WriteLEInt((int)curEntry.Crc); if (curEntry.LocalHeaderRequiresZip64) { stream.WriteLELong(curEntry.CompressedSize); stream.WriteLELong(curEntry.Size); offset += 24L; } else { stream.WriteLEInt((int)curEntry.CompressedSize); stream.WriteLEInt((int)curEntry.Size); offset += 16L; } } } protected byte[] InitializeAESPassword(ZipEntry entry, string rawPassword) { byte[] array = new byte[entry.AESSaltLen]; if (_aesRnd == null) { _aesRnd = RandomNumberGenerator.Create(); } _aesRnd.GetBytes(array); int blockSize = entry.AESKeySize / 8; cryptoTransform_ = new ZipAESTransform(rawPassword, array, blockSize, writeMode: true); byte[] array2 = new byte[array.Length + 2]; Array.Copy(array, array2, array.Length); Array.Copy(((ZipAESTransform)cryptoTransform_).PwdVerifier, 0, array2, array2.Length - 2, 2); return array2; } private byte[] CreateZipCryptoHeader(long crcValue) { offset += 12L; InitializeZipCryptoPassword(Password); byte[] array = new byte[12]; using (RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create()) { randomNumberGenerator.GetBytes(array); } array[11] = (byte)(crcValue >> 24); EncryptBlock(array, 0, array.Length); return array; } private void InitializeZipCryptoPassword(string password) { PkzipClassicManaged pkzipClassicManaged = new PkzipClassicManaged(); byte[] rgbKey = PkzipClassic.GenerateKeys(base.ZipCryptoEncoding.GetBytes(password)); cryptoTransform_ = pkzipClassicManaged.CreateEncryptor(rgbKey, null); } 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("buffer"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "Cannot be negative"); } if (count < 0) { throw new ArgumentOutOfRangeException("count", "Cannot be negative"); } if (buffer.Length - offset < count) { throw new ArgumentException("Invalid offset/count combination"); } if (curEntry.AESKeySize == 0 && !entryIsPassthrough) { 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) { byte[] array = new byte[4096]; while (count > 0) { int num = ((count < 4096) ? count : 4096); Array.Copy(buffer, offset, array, 0, num); EncryptBlock(array, 0, num); baseOutputStream_.Write(array, 0, num); count -= num; offset += num; } } public override void Finish() { if (entries == null) { return; } if (curEntry != null) { CloseEntry(); } long noOfEntries = entries.Count; long num = 0L; foreach (ZipEntry entry in entries) { num += ZipFormat.WriteEndEntry(baseOutputStream_, entry, _stringCodec); } ZipFormat.WriteEndOfCentralDirectory(baseOutputStream_, noOfEntries, num, offset, zipComment); entries = null; } public override async Task FinishAsync(CancellationToken ct) { using MemoryStream ms = new MemoryStream(); if (entries == null) { return; } if (curEntry != null) { await CloseEntryAsync(ct); } long numEntries = entries.Count; long sizeEntries = 0L; foreach (ZipEntry entry in entries) { await baseOutputStream_.WriteProcToStreamAsync(ms, delegate(Stream s) { sizeEntries += ZipFormat.WriteEndEntry(s, entry, _stringCodec); }, ct); } await baseOutputStream_.WriteProcToStreamAsync(ms, delegate(Stream s) { ZipFormat.WriteEndOfCentralDirectory(s, numEntries, sizeEntries, offset, zipComment); }, ct); entries = null; } public override void Flush() { if (curMethod == CompressionMethod.Stored) { baseOutputStream_.Flush(); } else { base.Flush(); } } } internal static class EncodingExtensions { public static bool IsZipUnicode(this Encoding e) { return e.Equals(StringCodec.UnicodeZipEncoding); } } public static class ZipStrings { private static readonly StringCodec CompatCodec = new StringCodec(); private static bool compatibilityMode; [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static int CodePage { get { return 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 { return !CompatCodec.ForceZipLegacyEncoding; } set { CompatCodec.ForceZipLegacyEncoding = !value; compatibilityMode = true; } } public static StringCodec GetStringCodec() { if (!compatibilityMode) { return new StringCodec(); } return CompatCodec; } [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] private static bool HasUnicodeFlag(int flags) { return ((GeneralBitFlags)flags).HasFlag(GeneralBitFlags.UnicodeText); } [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static string ConvertToString(byte[] data, int count) { return CompatCodec.ZipOutputEncoding.GetString(data, 0, count); } [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static string ConvertToString(byte[] data) { return CompatCodec.ZipOutputEncoding.GetString(data); } [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static string ConvertToStringExt(int flags, byte[] data, int count) { return CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data, 0, count); } [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static string ConvertToStringExt(int flags, byte[] data) { return CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data); } [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static byte[] ConvertToArray(string str) { return ConvertToArray(0, str); } [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static byte[] ConvertToArray(int flags, string str) { if (!string.IsNullOrEmpty(str)) { return CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetBytes(str); } return Empty.Array(); } } public class StringCodec { private Encoding _legacyEncoding = SystemDefaultEncoding; private Encoding _zipArchiveCommentEncoding; private Encoding _zipCryptoEncoding; public static readonly Encoding UnicodeZipEncoding; private const int FallbackCodePage = 437; public bool ForceZipLegacyEncoding { get; set; } public static Encoding DefaultZipCryptoEncoding => SystemDefaultEncoding; public Encoding ZipOutputEncoding => ZipEncoding(!ForceZipLegacyEncoding); public int CodePage { get { return _legacyEncoding.CodePage; } set { if (value < 4 || value > 65535 || value == 42) { throw new ArgumentOutOfRangeException("value"); } _legacyEncoding = Encoding.GetEncoding(value); } } public static int SystemDefaultCodePage { get; } public static Encoding SystemDefaultEncoding { get; } public Encoding ZipArchiveCommentEncoding { get { return _zipArchiveCommentEncoding ?? _legacyEncoding; } set { _zipArchiveCommentEncoding = value; } } public Encoding ZipCryptoEncoding { get { return _zipCryptoEncoding ?? DefaultZipCryptoEncoding; } set { _zipCryptoEncoding = value; } } static StringCodec() { UnicodeZipEncoding = Encoding.UTF8; try { int codePage = Encoding.Default.CodePage; SystemDefaultCodePage = ((codePage == 1 || codePage == 2 || codePage == 3 || codePage == 42) ? 437 : codePage); } catch { SystemDefaultCodePage = 437; } SystemDefaultEncoding = Encoding.GetEncoding(SystemDefaultCodePage); } public Encoding ZipEncoding(bool unicode) { if (!unicode) { return _legacyEncoding; } return UnicodeZipEncoding; } public Encoding ZipInputEncoding(GeneralBitFlags flags) { return ZipInputEncoding((int)flags); } public Encoding ZipInputEncoding(int flags) { return ZipEncoding(!ForceZipLegacyEncoding && (flags & 0x800) != 0); } } } namespace ICSharpCode.SharpZipLib.Zip.Compression { public class Deflater { public enum CompressionLevel { BEST_COMPRESSION = 9, BEST_SPEED = 1, DEFAULT_COMPRESSION = -1, NO_COMPRESSION = 0, DEFLATED = 8 } public const int BEST_COMPRESSION = 9; public const int BEST_SPEED = 1; public const int DEFAULT_COMPRESSION = -1; public const int NO_COMPRESSION = 0; public const int DEFLATED = 8; private const int IS_SETDICT = 1; private const int IS_FLUSHING = 4; private const int IS_FINISHING = 8; private const int INIT_STATE = 0; private const int SETDICT_STATE = 1; private const int BUSY_STATE = 16; private const int FLUSHING_STATE = 20; private const int FINISHING_STATE = 28; private const int FINISHED_STATE = 30; private const int CLOSED_STATE = 127; private int level; private bool noZlibHeaderOrFooter; private int state; private long totalOut; private DeflaterPending pending; private DeflaterEngine engine; public int Adler => engine.Adler; public long TotalIn => engine.TotalIn; public long TotalOut => totalOut; public bool IsFinished { get { if (state == 30) { return pending.IsFlushed; } return false; } } public bool IsNeedingInput => engine.NeedsInput(); public Deflater() : this(-1, noZlibHeaderOrFooter: false) { } public Deflater(int level) : this(level, noZlibHeaderOrFooter: false) { } public Deflater(int level, bool noZlibHeaderOrFooter) { switch (level) { case -1: level = 6; break; default: throw new ArgumentOutOfRangeException("level"); case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: break; } pending = new DeflaterPending(); engine = new DeflaterEngine(pending, noZlibHeaderOrFooter); this.noZlibHeaderOrFooter = noZlibHeaderOrFooter; SetStrategy(DeflateStrategy.Default); SetLevel(level); Reset(); } public void Reset() { state = (noZlibHeaderOrFooter ? 16 : 0); totalOut = 0L; pending.Reset(); engine.Reset(); } public void Flush() { state |= 4; } public void Finish() { state |= 12; } public void SetInput(byte[] input) { SetInput(input, 0, input.Length); } public void SetInput(byte[] input, int offset, int count) { if ((state & 8) != 0) { throw new InvalidOperationException("Finish() already called"); } engine.SetInput(input, offset, count); } public void SetLevel(int level) { switch (level) { case -1: level = 6; break; default: throw new ArgumentOutOfRangeException("level"); case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: break; } if (this.level != level) { this.level = level; engine.SetLevel(level); } } public int GetLevel() { return level; } public void SetStrategy(DeflateStrategy strategy) { engine.Strategy = strategy; } public int Deflate(byte[] output) { return Deflate(output, 0, output.Length); } public int Deflate(byte[] output, int offset, int length) { int num = length; if (state == 127) { throw new InvalidOperationException("Deflater closed"); } if (state < 16) { int num2 = 30720; int num3 = level - 1 >> 1; if (num3 < 0 || num3 > 3) { num3 = 3; } num2 |= num3 << 6; if ((state & 1) != 0) { num2 |= 0x20; } num2 += 31 - num2 % 31; pending.WriteShortMSB(num2); if ((state & 1) != 0) { int adler = engine.Adler; engine.ResetAdler(); pending.WriteShortMSB(adler >> 16); pending.WriteShortMSB(adler & 0xFFFF); } state = 0x10 | (state & 0xC); } while (true) { int num4 = pending.Flush(output, offset, length); offset += num4; totalOut += num4; length -= num4; if (length == 0 || state == 30) { break; } if (engine.Deflate((state & 4) != 0, (state & 8) != 0)) { continue; } switch (state) { case 16: return num - length; case 20: if (level != 0) { for (int num5 = 8 + (-pending.BitCount & 7); num5 > 0; num5 -= 10) { pending.WriteBits(2, 10); } } state = 16; break; case 28: pending.AlignToByte(); if (!noZlibHeaderOrFooter) { int adler2 = engine.Adler; pending.WriteShortMSB(adler2 >> 16); pending.WriteShortMSB(adler2 & 0xFFFF); } state = 30; break; } } return num - length; } public void SetDictionary(byte[] dictionary) { SetDictionary(dictionary, 0, dictionary.Length); } public void SetDictionary(byte[] dictionary, int index, int count) { if (state != 0) { throw new InvalidOperationException(); } state = 1; engine.SetDictionary(dictionary, index, count); } } public static class DeflaterConstants { public const bool DEBUGGING = false; public const int STORED_BLOCK = 0; public const int STATIC_TREES = 1; public const int DYN_TREES = 2; public const int PRESET_DICT = 32; public const int DEFAULT_MEM_LEVEL = 8; public const int MAX_MATCH = 258; public const int MIN_MATCH = 3; public const int MAX_WBITS = 15; public const int WSIZE = 32768; public const int WMASK = 32767; public const int HASH_BITS = 15; public const int HASH_SIZE = 32768; public const int HASH_MASK = 32767; public const int HASH_SHIFT = 5; public const int MIN_LOOKAHEAD = 262; public const int MAX_DIST = 32506; public const int PENDING_BUF_SIZE = 65536; public static int MAX_BLOCK_SIZE = Math.Min(65535, 65531); public const int DEFLATE_STORED = 0; public const int DEFLATE_FAST = 1; public const int DEFLATE_SLOW = 2; public static int[] GOOD_LENGTH = new int[10] { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 }; public static int[] MAX_LAZY = new int[10] { 0, 4, 5, 6, 4, 16, 16, 32, 128, 258 }; public static int[] NICE_LENGTH = new int[10] { 0, 8, 16, 32, 16, 32, 128, 128, 258, 258 }; public static int[] MAX_CHAIN = new int[10] { 0, 4, 8, 32, 16, 32, 128, 256, 1024, 4096 }; public static int[] COMPR_FUNC = new int[10] { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; } public enum DeflateStrategy { Default, Filtered, HuffmanOnly } public class DeflaterEngine { private const int TooFar = 4096; private int ins_h; private short[] head; private short[] prev; private int matchStart; private int matchLen; private bool prevAvailable; private int blockStart; private int strstart; private int lookahead; private byte[] window; private DeflateStrategy strategy; private int max_chain; private int max_lazy; private int niceLength; private int goodLength; private int compressionFunction; private byte[] inputBuf; private long totalIn; private int inputOff; private int inputEnd; private DeflaterPending pending; private DeflaterHuffman huffman; private Adler32 adler; public int Adler { get { if (adler == null) { return 0; } return (int)adler.Value; } } public long TotalIn => totalIn; public DeflateStrategy Strategy { get { return strategy; } set { strategy = value; } } public DeflaterEngine(DeflaterPending pending) : this(pending, noAdlerCalculation: false) { } public DeflaterEngine(DeflaterPending pending, bool noAdlerCalculation) { this.pending = pending; huffman = new DeflaterHuffman(pending); if (!noAdlerCalculation) { adler = new Adler32(); } window = new byte[65536]; head = new short[32768]; prev = new short[32768]; blockStart = (strstart = 1); } public bool Deflate(bool flush, bool finish) { bool flag; do { FillWindow(); bool flush2 = flush && inputOff == inputEnd; flag = compressionFunction switch { 0 => DeflateStored(flush2, finish), 1 => DeflateFast(flush2, finish), 2 => DeflateSlow(flush2, finish), _ => throw new InvalidOperationException("unknown compressionFunction"), }; } while (pending.IsFlushed && flag); return flag; } public void SetInput(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0) { throw new ArgumentOutOfRangeException("count"); } if (inputOff < inputEnd) { throw new InvalidOperationException("Old input was not completely processed"); } int num = offset + count; if (offset > num || num > buffer.Length) { throw new ArgumentOutOfRangeException("count"); } inputBuf = buffer; inputOff = offset; inputEnd = num; } public bool NeedsInput() { return inputEnd == inputOff; } public void SetDictionary(byte[] buffer, int offset, int length) { adler?.Update(new ArraySegment(buffer, offset, length)); if (length >= 3) { if (length > 32506) { offset += length - 32506; length = 32506; } Array.Copy(buffer, offset, window, strstart, length); UpdateHash(); length--; while (--length > 0) { InsertString(); strstart++; } strstart += 2; blockStart = strstart; } } public void Reset() { huffman.Reset(); adler?.Reset(); blockStart = (strstart = 1); lookahead = 0; totalIn = 0L; prevAvailable = false; matchLen = 2; for (int i = 0; i < 32768; i++) { head[i] = 0; } for (int j = 0; j < 32768; j++) { prev[j] = 0; } } public void ResetAdler() { adler?.Reset(); } public void SetLevel(int level) { if (level < 0 || level > 9) { throw new ArgumentOutOfRangeException("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) { return; } switch (compressionFunction) { case 0: if (strstart > blockStart) { huffman.FlushStoredBlock(window, blockStart, strstart - blockStart, lastBlock: false); blockStart = strstart; } UpdateHash(); break; case 1: if (strstart > blockStart) { huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock: false); blockStart = strstart; } break; case 2: if (prevAvailable) { huffman.TallyLit(window[strstart - 1] & 0xFF); } if (strstart > blockStart) { huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock: false); blockStart = strstart; } prevAvailable = false; matchLen = 2; break; } compressionFunction = DeflaterConstants.COMPR_FUNC[level]; } public void FillWindow() { if (strstart >= 65274) { SlideWindow(); } if (lookahead < 262 && inputOff < inputEnd) { int num = 65536 - lookahead - strstart; if (num > inputEnd - inputOff) { num = inputEnd - inputOff; } Array.Copy(inputBuf, inputOff, window, strstart + lookahead, num); adler?.Update(new ArraySegment(inputBuf, inputOff, num)); inputOff += num; totalIn += num; lookahead += num; } if (lookahead >= 3) { UpdateHash(); } } private void UpdateHash() { ins_h = (window[strstart] << 5) ^ window[strstart + 1]; } private int InsertString() { int num = ((ins_h << 5) ^ window[strstart + 2]) & 0x7FFF; short num2 = (prev[strstart & 0x7FFF] = head[num]); head[num] = (short)strstart; ins_h = num; return num2 & 0xFFFF; } private void SlideWindow() { Array.Copy(window, 32768, window, 0, 32768); matchStart -= 32768; strstart -= 32768; blockStart -= 32768; for (int i = 0; i < 32768; i++) { int num = head[i] & 0xFFFF; head[i] = (short)((num >= 32768) ? (num - 32768) : 0); } for (int j = 0; j < 32768; j++) { int num2 = prev[j] & 0xFFFF; prev[j] = (short)((num2 >= 32768) ? (num2 - 32768) : 0); } } private bool FindLongestMatch(int curMatch) { int num = strstart; int num2 = num + Math.Min(258, lookahead) - 1; int num3 = Math.Max(num - 32506, 0); byte[] array = window; short[] array2 = prev; int num4 = max_chain; int num5 = Math.Min(niceLength, lookahead); matchLen = Math.Max(matchLen, 2); if (num + matchLen > num2) { return false; } byte b = array[num + matchLen - 1]; byte b2 = array[num + matchLen]; if (matchLen >= goodLength) { num4 >>= 2; } do { int num6 = curMatch; num = strstart; if (array[num6 + matchLen] != b2 || array[num6 + matchLen - 1] != b || array[num6] != array[num] || array[++num6] != array[++num]) { continue; } switch ((num2 - num) % 8) { case 1: if (array[++num] != array[++num6]) { } break; case 2: if (array[++num] == array[++num6] && array[++num] != array[++num6]) { } break; case 3: if (array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] != array[++num6]) { } break; case 4: if (array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] != array[++num6]) { } break; case 5: if (array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] != array[++num6]) { } break; case 6: if (array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] != array[++num6]) { } break; case 7: if (array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6]) { _ = array[++num]; _ = array[++num6]; } break; } if (array[num] == array[num6]) { do { if (num == num2) { num++; num6++; break; } } while (array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6] && array[++num] == array[++num6]); } if (num - strstart > matchLen) { matchStart = curMatch; matchLen = num - strstart; if (matchLen >= num5) { break; } b = array[num - 1]; b2 = array[num]; } } while ((curMatch = array2[curMatch & 0x7FFF] & 0xFFFF) > num3 && --num4 != 0); return matchLen >= 3; } private bool DeflateStored(bool flush, bool finish) { if (!flush && lookahead == 0) { return false; } strstart += lookahead; lookahead = 0; int num = strstart - blockStart; if (num >= DeflaterConstants.MAX_BLOCK_SIZE || (blockStart < 32768 && num >= 32506) || flush) { bool flag = finish; if (num > DeflaterConstants.MAX_BLOCK_SIZE) { num = DeflaterConstants.MAX_BLOCK_SIZE; flag = false; } huffman.FlushStoredBlock(window, blockStart, num, flag); blockStart += num; if (!flag) { return num != 0; } return false; } return true; } private bool DeflateFast(bool flush, bool finish) { if (lookahead < 262 && !flush) { return false; } while (lookahead >= 262 || flush) { if (lookahead == 0) { huffman.FlushBlock(window, blockStart, strstart - blockStart, finish); blockStart = strstart; return false; } if (strstart > 65274) { SlideWindow(); } int num; if (lookahead >= 3 && (num = InsertString()) != 0 && strategy != DeflateStrategy.HuffmanOnly && strstart - num <= 32506 && FindLongestMatch(num)) { bool flag = huffman.TallyDist(strstart - matchStart, matchLen); lookahead -= matchLen; if (matchLen <= max_lazy && lookahead >= 3) { while (--matchLen > 0) { strstart++; InsertString(); } strstart++; } else { strstart += matchLen; if (lookahead >= 2) { UpdateHash(); } } matchLen = 2; if (!flag) { continue; } } else { huffman.TallyLit(window[strstart] & 0xFF); strstart++; lookahead--; } if (huffman.IsFull()) { bool flag2 = finish && lookahead == 0; huffman.FlushBlock(window, blockStart, strstart - blockStart, flag2); blockStart = strstart; return !flag2; } } return true; } private bool DeflateSlow(bool flush, bool finish) { if (lookahead < 262 && !flush) { return false; } while (lookahead >= 262 || flush) { if (lookahead == 0) { if (prevAvailable) { huffman.TallyLit(window[strstart - 1] & 0xFF); } prevAvailable = false; huffman.FlushBlock(window, blockStart, strstart - blockStart, finish); blockStart = strstart; return false; } if (strstart >= 65274) { SlideWindow(); } int num = matchStart; int num2 = matchLen; if (lookahead >= 3) { int num3 = InsertString(); if (strategy != DeflateStrategy.HuffmanOnly && num3 != 0 && strstart - num3 <= 32506 && FindLongestMatch(num3) && matchLen <= 5 && (strategy == DeflateStrategy.Filtered || (matchLen == 3 && strstart - matchStart > 4096))) { matchLen = 2; } } if (num2 >= 3 && matchLen <= num2) { huffman.TallyDist(strstart - 1 - num, num2); num2 -= 2; do { strstart++; lookahead--; if (lookahead >= 3) { InsertString(); } } while (--num2 > 0); strstart++; lookahead--; prevAvailable = false; matchLen = 2; } else { if (prevAvailable) { huffman.TallyLit(window[strstart - 1] & 0xFF); } prevAvailable = true; strstart++; lookahead--; } if (huffman.IsFull()) { int num4 = strstart - blockStart; if (prevAvailable) { num4--; } bool flag = finish && lookahead == 0 && !prevAvailable; huffman.FlushBlock(window, blockStart, num4, flag); blockStart += num4; return !flag; } } return true; } } public class DeflaterHuffman { private class Tree { 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; public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength) { this.dh = dh; minNumCodes = minCodes; this.maxLength = maxLength; freqs = new short[elems]; bl_counts = new int[maxLength]; } public void Reset() { for (int i = 0; i < freqs.Length; i++) { freqs[i] = 0; } codes = null; length = null; } public void WriteSymbol(int code) { dh.pending.WriteBits(codes[code] & 0xFFFF, length[code]); } public void CheckEmpty() { bool flag = true; for (int i = 0; i < freqs.Length; i++) { flag &= freqs[i] == 0; } if (!flag) { throw new SharpZipBaseException("!Empty"); } } public void SetStaticCodes(short[] staticCodes, byte[] staticLengths) { codes = staticCodes; length = staticLengths; } public void BuildCodes() { _ = freqs.Length; int[] array = new int[maxLength]; int num = 0; codes = new short[freqs.Length]; for (int i = 0; i < maxLength; i++) { array[i] = num; num += bl_counts[i] << 15 - i; } for (int j = 0; j < numCodes; j++) { int num2 = length[j]; if (num2 > 0) { codes[j] = BitReverse(array[num2 - 1]); array[num2 - 1] += 1 << 16 - num2; } } } public void BuildTree() { int num = freqs.Length; int[] array = new int[num]; int num2 = 0; int num3 = 0; for (int i = 0; i < num; i++) { int num4 = freqs[i]; if (num4 != 0) { int num5 = num2++; int num6; while (num5 > 0 && freqs[array[num6 = (num5 - 1) / 2]] > num4) { array[num5] = array[num6]; num5 = num6; } array[num5] = i; num3 = i; } } while (num2 < 2) { int num7 = ((num3 < 2) ? (++num3) : 0); array[num2++] = num7; } numCodes = Math.Max(num3 + 1, minNumCodes); int num8 = num2; int[] array2 = new int[4 * num2 - 2]; int[] array3 = new int[2 * num2 - 1]; int num9 = num8; for (int j = 0; j < num2; j++) { int num10 = (array2[2 * j] = array[j]); array2[2 * j + 1] = -1; array3[j] = freqs[num10] << 8; array[j] = j; } do { int num11 = array[0]; int num12 = array[--num2]; int num13 = 0; int num14; for (num14 = 1; num14 < num2; num14 = num14 * 2 + 1) { if (num14 + 1 < num2 && array3[array[num14]] > array3[array[num14 + 1]]) { num14++; } array[num13] = array[num14]; num13 = num14; } int num15 = array3[num12]; while ((num14 = num13) > 0 && array3[array[num13 = (num14 - 1) / 2]] > num15) { array[num14] = array[num13]; } array[num14] = num12; int num16 = array[0]; num12 = num9++; array2[2 * num12] = num11; array2[2 * num12 + 1] = num16; int num17 = Math.Min(array3[num11] & 0xFF, array3[num16] & 0xFF); num15 = (array3[num12] = array3[num11] + array3[num16] - num17 + 1); num13 = 0; for (num14 = 1; num14 < num2; num14 = num13 * 2 + 1) { if (num14 + 1 < num2 && array3[array[num14]] > array3[array[num14 + 1]]) { num14++; } array[num13] = array[num14]; num13 = num14; } while ((num14 = num13) > 0 && array3[array[num13 = (num14 - 1) / 2]] > num15) { array[num14] = array[num13]; } array[num14] = num12; } while (num2 > 1); if (array[0] != array2.Length / 2 - 1) { throw new SharpZipBaseException("Heap invariant violated"); } BuildLength(array2); } public int GetEncodedLength() { int num = 0; for (int i = 0; i < freqs.Length; i++) { num += freqs[i] * length[i]; } return num; } public void CalcBLFreq(Tree blTree) { int num = -1; int num2 = 0; while (num2 < numCodes) { int num3 = 1; int num4 = length[num2]; int num5; int num6; if (num4 == 0) { num5 = 138; num6 = 3; } else { num5 = 6; num6 = 3; if (num != num4) { blTree.freqs[num4]++; num3 = 0; } } num = num4; num2++; while (num2 < numCodes && num == length[num2]) { num2++; if (++num3 >= num5) { break; } } if (num3 < num6) { blTree.freqs[num] += (short)num3; } else if (num != 0) { blTree.freqs[16]++; } else if (num3 <= 10) { blTree.freqs[17]++; } else { blTree.freqs[18]++; } } } public void WriteTree(Tree blTree) { int num = -1; int num2 = 0; while (num2 < numCodes) { int num3 = 1; int num4 = length[num2]; int num5; int num6; if (num4 == 0) { num5 = 138; num6 = 3; } else { num5 = 6; num6 = 3; if (num != num4) { blTree.WriteSymbol(num4); num3 = 0; } } num = num4; num2++; while (num2 < numCodes && num == length[num2]) { num2++; if (++num3 >= num5) { break; } } if (num3 < num6) { while (num3-- > 0) { blTree.WriteSymbol(num); } } else if (num != 0) { blTree.WriteSymbol(16); dh.pending.WriteBits(num3 - 3, 2); } else if (num3 <= 10) { blTree.WriteSymbol(17); dh.pending.WriteBits(num3 - 3, 3); } else { blTree.WriteSymbol(18); dh.pending.WriteBits(num3 - 11, 7); } } } private void BuildLength(int[] childs) { length = new byte[freqs.Length]; int num = childs.Length / 2; int num2 = (num + 1) / 2; int num3 = 0; for (int i = 0; i < maxLength; i++) { bl_counts[i] = 0; } int[] array = new int[num]; array[num - 1] = 0; for (int num4 = num - 1; num4 >= 0; num4--) { if (childs[2 * num4 + 1] != -1) { int num5 = array[num4] + 1; if (num5 > maxLength) { num5 = maxLength; num3++; } array[childs[2 * num4]] = (array[childs[2 * num4 + 1]] = num5); } else { int num6 = array[num4]; bl_counts[num6 - 1]++; length[childs[2 * num4]] = (byte)array[num4]; } } if (num3 == 0) { return; } int num7 = maxLength - 1; while (true) { if (bl_counts[--num7] != 0) { do { bl_counts[num7]--; bl_counts[++num7]++; num3 -= 1 << maxLength - 1 - num7; } while (num3 > 0 && num7 < maxLength - 1); if (num3 <= 0) { break; } } } bl_counts[maxLength - 1] += num3; bl_counts[maxLength - 2] -= num3; int num8 = 2 * num2; for (int num9 = maxLength; num9 != 0; num9--) { int num10 = bl_counts[num9 - 1]; while (num10 > 0) { int num11 = 2 * childs[num8++]; if (childs[num11 + 1] == -1) { length[childs[num11]] = (byte)num9; num10--; } } } } } private const int BUFSIZE = 16384; private const int LITERAL_NUM = 286; private const int DIST_NUM = 30; private const int BITLEN_NUM = 19; private const int REP_3_6 = 16; private const int REP_3_10 = 17; private const int REP_11_138 = 18; private const int EOF_SYMBOL = 256; private static readonly int[] BL_ORDER; private static readonly byte[] bit4Reverse; private static short[] staticLCodes; private static byte[] staticLLength; private static short[] staticDCodes; private static byte[] staticDLength; public DeflaterPending pending; private Tree literalTree; private Tree distTree; private Tree blTree; private short[] d_buf; private byte[] l_buf; private int last_lit; private int extra_bits; static DeflaterHuffman() { BL_ORDER = new int[19] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; bit4Reverse = new byte[16] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; staticLCodes = new short[286]; staticLLength = new byte[286]; int num = 0; while (num < 144) { staticLCodes[num] = BitReverse(48 + num << 8); staticLLength[num++] = 8; } while (num < 256) { staticLCodes[num] = BitReverse(256 + num << 7); staticLLength[num++] = 9; } while (num < 280) { staticLCodes[num] = BitReverse(-256 + num << 9); staticLLength[num++] = 7; } while (num < 286) { staticLCodes[num] = BitReverse(-88 + num << 8); staticLLength[num++] = 8; } staticDCodes = new short[30]; staticDLength = new byte[30]; for (num = 0; num < 30; num++) { staticDCodes[num] = BitReverse(num << 11); staticDLength[num] = 5; } } public DeflaterHuffman(DeflaterPending pending) { this.pending = pending; literalTree = new Tree(this, 286, 257, 15); distTree = new Tree(this, 30, 1, 15); blTree = new Tree(this, 19, 4, 7); d_buf = new short[16384]; l_buf = new byte[16384]; } public void Reset() { last_lit = 0; extra_bits = 0; literalTree.Reset(); distTree.Reset(); blTree.Reset(); } 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 i = 0; i < blTreeCodes; i++) { pending.WriteBits(blTree.length[BL_ORDER[i]], 3); } literalTree.WriteTree(blTree); distTree.WriteTree(blTree); } public void CompressBlock() { for (int i = 0; i < last_lit; i++) { int num = l_buf[i] & 0xFF; int num2 = d_buf[i]; if (num2-- != 0) { int num3 = Lcode(num); literalTree.WriteSymbol(num3); int num4 = (num3 - 261) / 4; if (num4 > 0 && num4 <= 5) { pending.WriteBits(num & ((1 << num4) - 1), num4); } int num5 = Dcode(num2); distTree.WriteSymbol(num5); num4 = num5 / 2 - 1; if (num4 > 0) { pending.WriteBits(num2 & ((1 << num4) - 1), num4); } } else { literalTree.WriteSymbol(num); } } literalTree.WriteSymbol(256); } public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) { pending.WriteBits(lastBlock ? 1 : 0, 3); pending.AlignToByte(); pending.WriteShort(storedLength); pending.WriteShort(~storedLength); pending.WriteBlock(stored, storedOffset, storedLength); Reset(); } public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) { literalTree.freqs[256]++; literalTree.BuildTree(); distTree.BuildTree(); literalTree.CalcBLFreq(blTree); distTree.CalcBLFreq(blTree); blTree.BuildTree(); int num = 4; for (int num2 = 18; num2 > num; num2--) { if (blTree.length[BL_ORDER[num2]] > 0) { num = num2 + 1; } } int num3 = 14 + num * 3 + blTree.GetEncodedLength() + literalTree.GetEncodedLength() + distTree.GetEncodedLength() + extra_bits; int num4 = extra_bits; for (int i = 0; i < 286; i++) { num4 += literalTree.freqs[i] * staticLLength[i]; } for (int j = 0; j < 30; j++) { num4 += distTree.freqs[j] * staticDLength[j]; } if (num3 >= num4) { num3 = num4; } if (storedOffset >= 0 && storedLength + 4 < num3 >> 3) { FlushStoredBlock(stored, storedOffset, storedLength, lastBlock); } else if (num3 == num4) { pending.WriteBits(2 + (lastBlock ? 1 : 0), 3); literalTree.SetStaticCodes(staticLCodes, staticLLength); distTree.SetStaticCodes(staticDCodes, staticDLength); CompressBlock(); Reset(); } else { pending.WriteBits(4 + (lastBlock ? 1 : 0), 3); SendAllTrees(num); CompressBlock(); Reset(); } } public bool IsFull() { return last_lit >= 16384; } public bool TallyLit(int literal) { d_buf[last_lit] = 0; l_buf[last_lit++] = (byte)literal; literalTree.freqs[literal]++; return IsFull(); } public bool TallyDist(int distance, int length) { d_buf[last_lit] = (short)distance; l_buf[last_lit++] = (byte)(length - 3); int num = Lcode(length - 3); literalTree.freqs[num]++; if (num >= 265 && num < 285) { extra_bits += (num - 261) / 4; } int num2 = Dcode(distance - 1); distTree.freqs[num2]++; if (num2 >= 4) { extra_bits += num2 / 2 - 1; } return IsFull(); } 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 num = 257; while (length >= 8) { num += 4; length >>= 1; } return num + length; } private static int Dcode(int distance) { int num = 0; while (distance >= 4) { num += 2; distance >>= 1; } return num + distance; } } public class DeflaterPending : PendingBuffer { public DeflaterPending() : base(65536) { } } public class Inflater { private static readonly int[] CPLENS = new int[29] { 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 }; private static readonly int[] CPLEXT = new int[29] { 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 }; private static readonly int[] CPDIST = new int[30] { 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 }; private static readonly int[] CPDEXT = new int[30] { 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 }; 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; private int mode; private int readAdler; private int neededBits; private int repLength; private int repDist; private int uncomprLen; private bool isLastBlock; private long totalOut; private long totalIn; private bool noHeader; private readonly StreamManipulator input; private OutputWindow outputWindow; private InflaterDynHeader dynHeader; private InflaterHuffmanTree litlenTree; private InflaterHuffmanTree distTree; private Adler32 adler; public bool IsNeedingInput => input.IsNeedingInput; public bool IsNeedingDictionary { get { if (mode == 1) { return neededBits == 0; } return false; } } public bool IsFinished { get { if (mode == 12) { return outputWindow.GetAvailable() == 0; } return false; } } public int Adler { get { if (IsNeedingDictionary) { return readAdler; } if (adler != null) { return (int)adler.Value; } return 0; } } public long TotalOut => totalOut; public long TotalIn => totalIn - RemainingInput; public int RemainingInput => input.AvailableBytes; public Inflater() : this(noHeader: false) { } public Inflater(bool noHeader) { this.noHeader = noHeader; if (!noHeader) { adler = new Adler32(); } input = new StreamManipulator(); outputWindow = new OutputWindow(); mode = (noHeader ? 2 : 0); } public void Reset() { mode = (noHeader ? 2 : 0); totalIn = 0L; totalOut = 0L; input.Reset(); outputWindow.Reset(); dynHeader = null; litlenTree = null; distTree = null; isLastBlock = false; adler?.Reset(); } private bool DecodeHeader() { int num = input.PeekBits(16); if (num < 0) { return false; } input.DropBits(16); num = ((num << 8) | (num >> 8)) & 0xFFFF; if (num % 31 != 0) { throw new SharpZipBaseException("Header checksum illegal"); } if ((num & 0xF00) != 2048) { throw new SharpZipBaseException("Compression Method unknown"); } if ((num & 0x20) == 0) { mode = 2; } else { mode = 1; neededBits = 32; } return true; } private bool DecodeDict() { while (neededBits > 0) { int num = input.PeekBits(8); if (num < 0) { return false; } input.DropBits(8); readAdler = (readAdler << 8) | num; neededBits -= 8; } return false; } private bool DecodeHuffman() { int num = outputWindow.GetFreeSpace(); while (num >= 258) { switch (mode) { case 7: { int symbol; while (((symbol = litlenTree.GetSymbol(input)) & -256) == 0) { outputWindow.Write(symbol); if (--num < 258) { return true; } } if (symbol < 257) { if (symbol < 0) { return false; } distTree = null; litlenTree = null; mode = 2; return true; } try { repLength = CPLENS[symbol - 257]; neededBits = CPLEXT[symbol - 257]; } catch (Exception) { throw new SharpZipBaseException("Illegal rep length code"); } goto case 8; } case 8: if (neededBits > 0) { mode = 8; int num2 = input.PeekBits(neededBits); if (num2 < 0) { return false; } input.DropBits(neededBits); repLength += num2; } mode = 9; goto case 9; case 9: { int 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 10; } case 10: if (neededBits > 0) { mode = 10; int num3 = input.PeekBits(neededBits); if (num3 < 0) { return false; } input.DropBits(neededBits); repDist += num3; } break; default: throw new SharpZipBaseException("Inflater unknown mode"); } outputWindow.Repeat(repLength, repDist); num -= repLength; mode = 7; } return true; } private bool DecodeChksum() { while (neededBits > 0) { int num = input.PeekBits(8); if (num < 0) { return false; } input.DropBits(8); readAdler = (readAdler << 8) | num; neededBits -= 8; } if ((int)(adler?.Value).Value != readAdler) { throw new SharpZipBaseException("Adler chksum doesn't match: " + (int)(adler?.Value).Value + " vs. " + readAdler); } mode = 12; return false; } private bool Decode() { switch (mode) { case 0: return DecodeHeader(); case 1: return DecodeDict(); case 11: return DecodeChksum(); case 2: { if (isLastBlock) { if (noHeader) { mode = 12; return false; } input.SkipToByteBoundary(); neededBits = 32; mode = 11; return true; } int num = input.PeekBits(3); if (num < 0) { return false; } input.DropBits(3); isLastBlock |= (num & 1) != 0; switch (num >> 1) { case 0: input.SkipToByteBoundary(); mode = 3; break; case 1: litlenTree = InflaterHuffmanTree.defLitLenTree; distTree = InflaterHuffmanTree.defDistTree; mode = 7; break; case 2: dynHeader = new InflaterDynHeader(input); mode = 6; break; default: throw new SharpZipBaseException("Unknown block type " + num); } return true; } case 3: if ((uncomprLen = input.PeekBits(16)) < 0) { return false; } input.DropBits(16); mode = 4; goto case 4; case 4: { int num3 = input.PeekBits(16); if (num3 < 0) { return false; } input.DropBits(16); if (num3 != (uncomprLen ^ 0xFFFF)) { throw new SharpZipBaseException("broken uncompressed block"); } mode = 5; goto case 5; } case 5: { int num2 = outputWindow.CopyStored(input, uncomprLen); uncomprLen -= num2; if (uncomprLen == 0) { mode = 2; return true; } return !input.IsNeedingInput; } case 6: if (!dynHeader.AttemptRead()) { return false; } litlenTree = dynHeader.LiteralLengthTree; distTree = dynHeader.DistanceTree; mode = 7; goto case 7; case 7: case 8: case 9: case 10: return DecodeHuffman(); case 12: return false; default: throw new SharpZipBaseException("Inflater.Decode unknown mode"); } } public void SetDictionary(byte[] buffer) { SetDictionary(buffer, 0, buffer.Length); } public void SetDictionary(byte[] buffer, int index, int count) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (index < 0) { throw new ArgumentOutOfRangeException("index"); } if (count < 0) { throw new ArgumentOutOfRangeException("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 = 2; } public void SetInput(byte[] buffer) { SetInput(buffer, 0, buffer.Length); } public void SetInput(byte[] buffer, int index, int count) { input.SetInput(buffer, index, count); totalIn += count; } public int Inflate(byte[] buffer) { if (buffer == null) { throw new ArgumentNullException("buffer"); } return Inflate(buffer, 0, buffer.Length); } public int Inflate(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (count < 0) { throw new ArgumentOutOfRangeException("count", "count cannot be negative"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "offset cannot be negative"); } if (offset + count > buffer.Length) { throw new ArgumentException("count exceeds buffer bounds"); } if (count == 0) { if (!IsFinished) { Decode(); } return 0; } int num = 0; do { if (mode == 11) { continue; } int num2 = outputWindow.CopyOutput(buffer, offset, count); if (num2 > 0) { adler?.Update(new ArraySegment(buffer, offset, num2)); offset += num2; num += num2; totalOut += num2; count -= num2; if (count == 0) { return num; } } } while (Decode() || (outputWindow.GetAvailable() > 0 && mode != 11)); return num; } } internal class InflaterDynHeader { private const int LITLEN_MAX = 286; private const int DIST_MAX = 30; private const int CODELEN_MAX = 316; private const int META_MAX = 19; private static readonly int[] MetaCodeLengthIndex = new int[19] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; private readonly StreamManipulator input; private readonly IEnumerator state; private readonly IEnumerable stateMachine; private byte[] codeLengths = new byte[316]; private InflaterHuffmanTree litLenTree; private InflaterHuffmanTree distTree; private int litLenCodeCount; private int distanceCodeCount; private int metaCodeCount; public InflaterHuffmanTree LiteralLengthTree => litLenTree ?? throw new StreamDecodingException("Header properties were accessed before header had been successfully read"); public InflaterHuffmanTree DistanceTree => distTree ?? throw new StreamDecodingException("Header properties were accessed before header had been successfully read"); public bool AttemptRead() { if (state.MoveNext()) { return state.Current; } return true; } public InflaterDynHeader(StreamManipulator input) { this.input = input; stateMachine = CreateStateMachine(); state = stateMachine.GetEnumerator(); } private IEnumerable CreateStateMachine() { 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; } int dataCodeCount = litLenCodeCount + distanceCodeCount; if (litLenCodeCount > 286) { throw new ValueOutOfRangeException("litLenCodeCount"); } if (distanceCodeCount > 30) { throw new ValueOutOfRangeException("distanceCodeCount"); } if (metaCodeCount > 19) { throw new ValueOutOfRangeException("metaCodeCount"); } for (int i = 0; i < metaCodeCount; i++) { while (!input.TryGetBits(3, ref codeLengths, MetaCodeLengthIndex[i])) { yield return false; } } InflaterHuffmanTree metaCodeTree = new InflaterHuffmanTree(codeLengths); int index = 0; while (index < dataCodeCount) { int symbol; while ((symbol = metaCodeTree.GetSymbol(input)) < 0) { yield return false; } if (symbol < 16) { codeLengths[index++] = (byte)symbol; continue; } int i = 0; byte codeLength; switch (symbol) { case 16: if (index == 0) { throw new StreamDecodingException("Cannot repeat previous code length when no other code length has been read"); } codeLength = codeLengths[index - 1]; while (!input.TryGetBits(2, ref i, 3)) { yield return false; } break; case 17: codeLength = 0; while (!input.TryGetBits(3, ref i, 3)) { yield return false; } break; default: codeLength = 0; while (!input.TryGetBits(7, ref i, 11)) { yield return false; } break; } if (index + i > dataCodeCount) { throw new StreamDecodingException("Cannot repeat code lengths past total number of data code lengths"); } while (i-- > 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; } } public class InflaterHuffmanTree { private const int MAX_BITLEN = 15; private short[] tree; public static InflaterHuffmanTree defLitLenTree; public static InflaterHuffmanTree defDistTree; static InflaterHuffmanTree() { try { byte[] array = new byte[288]; int num = 0; while (num < 144) { array[num++] = 8; } while (num < 256) { array[num++] = 9; } while (num < 280) { array[num++] = 7; } while (num < 288) { array[num++] = 8; } defLitLenTree = new InflaterHuffmanTree(array); array = new byte[32]; num = 0; while (num < 32) { array[num++] = 5; } defDistTree = new InflaterHuffmanTree(array); } catch (Exception) { throw new SharpZipBaseException("InflaterHuffmanTree: static tree length illegal"); } } public InflaterHuffmanTree(IList codeLengths) { BuildTree(codeLengths); } private void BuildTree(IList codeLengths) { int[] array = new int[16]; int[] array2 = new int[16]; for (int i = 0; i < codeLengths.Count; i++) { int num = codeLengths[i]; if (num > 0) { array[num]++; } } int num2 = 0; int num3 = 512; for (int j = 1; j <= 15; j++) { array2[j] = num2; num2 += array[j] << 16 - j; if (j >= 10) { int num4 = array2[j] & 0x1FF80; int num5 = num2 & 0x1FF80; num3 += num5 - num4 >> 16 - j; } } tree = new short[num3]; int num6 = 512; for (int num7 = 15; num7 >= 10; num7--) { int num8 = num2 & 0x1FF80; num2 -= array[num7] << 16 - num7; for (int k = num2 & 0x1FF80; k < num8; k += 128) { tree[DeflaterHuffman.BitReverse(k)] = (short)((-num6 << 4) | num7); num6 += 1 << num7 - 9; } } for (int l = 0; l < codeLengths.Count; l++) { int num9 = codeLengths[l]; if (num9 == 0) { continue; } num2 = array2[num9]; int num10 = DeflaterHuffman.BitReverse(num2); if (num9 <= 9) { do { tree[num10] = (short)((l << 4) | num9); num10 += 1 << num9; } while (num10 < 512); } else { int num11 = tree[num10 & 0x1FF]; int num12 = 1 << (num11 & 0xF); num11 = -(num11 >> 4); do { tree[num11 | (num10 >> 9)] = (short)((l << 4) | num9); num10 += 1 << num9; } while (num10 < num12); } array2[num9] = num2 + (1 << 16 - num9); } } public int GetSymbol(StreamManipulator input) { int num; int num2; if ((num = input.PeekBits(9)) >= 0) { num2 = tree[num]; int num3 = num2 & 0xF; if (num2 >= 0) { if (num3 == 0) { throw new SharpZipBaseException("Encountered invalid codelength 0"); } input.DropBits(num3); return num2 >> 4; } int num4 = -(num2 >> 4); if ((num = input.PeekBits(num3)) >= 0) { num2 = tree[num4 | (num >> 9)]; input.DropBits(num2 & 0xF); return num2 >> 4; } int availableBits = input.AvailableBits; num = input.PeekBits(availableBits); num2 = tree[num4 | (num >> 9)]; if ((num2 & 0xF) <= availableBits) { input.DropBits(num2 & 0xF); return num2 >> 4; } return -1; } int availableBits2 = input.AvailableBits; num = input.PeekBits(availableBits2); num2 = tree[num]; if (num2 >= 0 && (num2 & 0xF) <= availableBits2) { input.DropBits(num2 & 0xF); return num2 >> 4; } return -1; } } public class PendingBuffer { private readonly byte[] buffer; private int start; private int end; private uint bits; private int bitCount; public int BitCount => bitCount; public bool IsFlushed => end == 0; public PendingBuffer() : this(4096) { } public PendingBuffer(int bufferSize) { buffer = new byte[bufferSize]; } public void Reset() { start = (end = (bitCount = 0)); } public void WriteByte(int value) { buffer[end++] = (byte)value; } public void WriteShort(int value) { buffer[end++] = (byte)value; buffer[end++] = (byte)(value >> 8); } public void WriteInt(int value) { buffer[end++] = (byte)value; buffer[end++] = (byte)(value >> 8); buffer[end++] = (byte)(value >> 16); buffer[end++] = (byte)(value >> 24); } public void WriteBlock(byte[] block, int offset, int length) { Array.Copy(block, offset, buffer, end, length); end += length; } public void AlignToByte() { if (bitCount > 0) { buffer[end++] = (byte)bits; if (bitCount > 8) { buffer[end++] = (byte)(bits >> 8); } } bits = 0u; bitCount = 0; } public void WriteBits(int b, int count) { bits |= (uint)(b << bitCount); bitCount += count; if (bitCount >= 16) { buffer[end++] = (byte)bits; buffer[end++] = (byte)(bits >> 8); bits >>= 16; bitCount -= 16; } } public void WriteShortMSB(int s) { buffer[end++] = (byte)(s >> 8); buffer[end++] = (byte)s; } public int Flush(byte[] output, int offset, int length) { if (bitCount >= 8) { buffer[end++] = (byte)bits; bits >>= 8; bitCount -= 8; } if (length > end - start) { length = end - start; Array.Copy(buffer, start, output, offset, length); start = 0; end = 0; } else { Array.Copy(buffer, start, output, offset, length); start += length; } return length; } public byte[] ToByteArray() { AlignToByte(); byte[] array = new byte[end - start]; Array.Copy(buffer, start, array, 0, array.Length); start = 0; end = 0; return array; } } } namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams { public class DeflaterOutputStream : Stream { protected ICryptoTransform cryptoTransform_; protected byte[] AESAuthCode; private byte[] buffer_; protected Deflater deflater_; protected Stream baseOutputStream_; private bool isClosed_; public bool IsStreamOwner { get; set; } = true; public bool CanPatchEntries => baseOutputStream_.CanSeek; public Encoding ZipCryptoEncoding { get; set; } = StringCodec.DefaultZipCryptoEncoding; public override bool CanRead => false; public override bool CanSeek => false; public override bool CanWrite => baseOutputStream_.CanWrite; public override long Length => baseOutputStream_.Length; public override long Position { get { return baseOutputStream_.Position; } set { throw new NotSupportedException("Position property not supported"); } } public DeflaterOutputStream(Stream baseOutputStream) : this(baseOutputStream, new Deflater(), 512) { } public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater) : this(baseOutputStream, deflater, 512) { } public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize) { if (baseOutputStream == null) { throw new ArgumentNullException("baseOutputStream"); } if (!baseOutputStream.CanWrite) { throw new ArgumentException("Must support writing", "baseOutputStream"); } if (bufferSize < 512) { throw new ArgumentOutOfRangeException("bufferSize"); } baseOutputStream_ = baseOutputStream; buffer_ = new byte[bufferSize]; deflater_ = deflater ?? throw new ArgumentNullException("deflater"); } public virtual void Finish() { deflater_.Finish(); while (!deflater_.IsFinished) { int num = deflater_.Deflate(buffer_, 0, buffer_.Length); if (num <= 0) { break; } EncryptBlock(buffer_, 0, num); baseOutputStream_.Write(buffer_, 0, num); } 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; } } public virtual async Task FinishAsync(CancellationToken ct) { deflater_.Finish(); while (!deflater_.IsFinished) { int num = deflater_.Deflate(buffer_, 0, buffer_.Length); if (num <= 0) { break; } EncryptBlock(buffer_, 0, num); await baseOutputStream_.WriteAsync(buffer_, 0, num, 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; } } protected void EncryptBlock(byte[] buffer, int offset, int length) { if (cryptoTransform_ != null) { cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0); } } protected void Deflate() { Deflate(flushing: false); } private void Deflate(bool flushing) { while (flushing || !deflater_.IsNeedingInput) { int num = deflater_.Deflate(buffer_, 0, buffer_.Length); if (num <= 0) { break; } EncryptBlock(buffer_, 0, num); baseOutputStream_.Write(buffer_, 0, num); } if (!deflater_.IsNeedingInput) { throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?"); } } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException("DeflaterOutputStream Seek not supported"); } public override void SetLength(long value) { throw new NotSupportedException("DeflaterOutputStream SetLength not supported"); } public override int ReadByte() { throw new NotSupportedException("DeflaterOutputStream ReadByte not supported"); } public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException("DeflaterOutputStream Read not supported"); } public override void Flush() { deflater_.Flush(); Deflate(flushing: true); baseOutputStream_.Flush(); } protected override void Dispose(bool disposing) { if (isClosed_) { return; } isClosed_ = true; try { Finish(); if (cryptoTransform_ != null) { GetAuthCodeIfAES(); cryptoTransform_.Dispose(); cryptoTransform_ = null; } } finally { if (IsStreamOwner) { baseOutputStream_.Dispose(); } } } protected void GetAuthCodeIfAES() { if (cryptoTransform_ is ZipAESTransform) { AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); } } public override void WriteByte(byte value) { Write(new byte[1] { value }, 0, 1); } public override void Write(byte[] buffer, int offset, int count) { deflater_.SetInput(buffer, offset, count); Deflate(); } } public class InflaterInputBuffer { private int rawLength; private byte[] rawData; private int clearTextLength; private byte[] clearText; private byte[] internalClearText; private int available; private ICryptoTransform cryptoTransform; private Stream inputStream; public int RawLength => rawLength; public byte[] RawData => rawData; public int ClearTextLength => clearTextLength; public byte[] ClearText => clearText; public int Available { get { return available; } set { available = value; } } 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; } } } public InflaterInputBuffer(Stream stream) : this(stream, 4096) { } public InflaterInputBuffer(Stream stream, int bufferSize) { inputStream = stream; if (bufferSize < 1024) { bufferSize = 1024; } rawData = new byte[bufferSize]; clearText = rawData; } public void SetInflaterInput(Inflater inflater) { if (available > 0) { inflater.SetInput(clearText, clearTextLength - available, available); available = 0; } } public void Fill() { rawLength = 0; int num = rawData.Length; while (num > 0 && inputStream.CanRead) { int num2 = inputStream.Read(rawData, rawLength, num); if (num2 <= 0) { break; } rawLength += num2; num -= num2; } if (cryptoTransform != null) { clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0); } else { clearTextLength = rawLength; } available = clearTextLength; } public int ReadRawBuffer(byte[] buffer) { return ReadRawBuffer(buffer, 0, buffer.Length); } public int ReadRawBuffer(byte[] outBuffer, int offset, int length) { if (length < 0) { throw new ArgumentOutOfRangeException("length"); } int num = offset; int num2 = length; while (num2 > 0) { if (available <= 0) { Fill(); if (available <= 0) { return 0; } } int num3 = Math.Min(num2, available); Array.Copy(rawData, rawLength - available, outBuffer, num, num3); num += num3; num2 -= num3; available -= num3; } return length; } public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length) { if (length < 0) { throw new ArgumentOutOfRangeException("length"); } int num = offset; int num2 = length; while (num2 > 0) { if (available <= 0) { Fill(); if (available <= 0) { return 0; } } int num3 = Math.Min(num2, available); Array.Copy(clearText, clearTextLength - available, outBuffer, num, num3); num += num3; num2 -= num3; available -= num3; } return length; } public byte ReadLeByte() { if (available <= 0) { Fill(); if (available <= 0) { throw new ZipException("EOF in header"); } } byte result = rawData[rawLength - available]; available--; return result; } public int ReadLeShort() { return ReadLeByte() | (ReadLeByte() << 8); } public int ReadLeInt() { return ReadLeShort() | (ReadLeShort() << 16); } public long ReadLeLong() { return (uint)ReadLeInt() | ((long)ReadLeInt() << 32); } } public class InflaterInputStream : Stream { protected Inflater inf; protected InflaterInputBuffer inputBuffer; private Stream baseInputStream; protected long csize; private bool isClosed; public bool IsStreamOwner { get; set; } = true; public virtual int Available { get { if (!inf.IsFinished) { return 1; } return 0; } } public override bool CanRead => baseInputStream.CanRead; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length { get { throw new NotSupportedException("InflaterInputStream Length is not supported"); } } public override long Position { get { return baseInputStream.Position; } set { throw new NotSupportedException("InflaterInputStream Position not supported"); } } public InflaterInputStream(Stream baseInputStream) : this(baseInputStream, new Inflater(), 4096) { } public InflaterInputStream(Stream baseInputStream, Inflater inf) : this(baseInputStream, inf, 4096) { } public InflaterInputStream(Stream baseInputStream, Inflater inflater, int bufferSize) { if (baseInputStream == null) { throw new ArgumentNullException("baseInputStream"); } if (inflater == null) { throw new ArgumentNullException("inflater"); } if (bufferSize <= 0) { throw new ArgumentOutOfRangeException("bufferSize"); } this.baseInputStream = baseInputStream; inf = inflater; inputBuffer = new InflaterInputBuffer(baseInputStream, bufferSize); } public long Skip(long count) { if (count <= 0) { throw new ArgumentOutOfRangeException("count"); } if (baseInputStream.CanSeek) { baseInputStream.Seek(count, SeekOrigin.Current); return count; } int num = 2048; if (count < num) { num = (int)count; } byte[] buffer = new byte[num]; int num2 = 1; long num3 = count; while (num3 > 0 && num2 > 0) { if (num3 < num) { num = (int)num3; } num2 = baseInputStream.Read(buffer, 0, num); num3 -= num2; } return count - num3; } protected void StopDecrypting() { inputBuffer.CryptoTransform = null; } protected void Fill() { if (inputBuffer.Available <= 0) { inputBuffer.Fill(); if (inputBuffer.Available <= 0) { throw new SharpZipBaseException("Unexpected EOF"); } } inputBuffer.SetInflaterInput(inf); } public override void Flush() { baseInputStream.Flush(); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException("Seek not supported"); } public override void SetLength(long value) { throw new NotSupportedException("InflaterInputStream SetLength not supported"); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException("InflaterInputStream Write not supported"); } public override void WriteByte(byte value) { throw new NotSupportedException("InflaterInputStream WriteByte not supported"); } protected override void Dispose(bool disposing) { if (!isClosed) { isClosed = true; if (IsStreamOwner) { baseInputStream.Dispose(); } } } public override int Read(byte[] buffer, int offset, int count) { if (inf.IsNeedingDictionary) { throw new SharpZipBaseException("Need a dictionary"); } int num = count; while (true) { int num2 = inf.Inflate(buffer, offset, num); offset += num2; num -= num2; if (num == 0 || inf.IsFinished) { break; } if (inf.IsNeedingInput) { Fill(); } else if (num2 == 0) { throw new ZipException("Invalid input data"); } } return count - num; } } public class OutputWindow { private const int WindowSize = 32768; private const int WindowMask = 32767; private byte[] window = new byte[32768]; private int windowEnd; private int windowFilled; public void Write(int value) { if (windowFilled++ == 32768) { throw new InvalidOperationException("Window full"); } window[windowEnd++] = (byte)value; windowEnd &= 32767; } private void SlowRepeat(int repStart, int length, int distance) { while (length-- > 0) { window[windowEnd++] = window[repStart++]; windowEnd &= 32767; repStart &= 0x7FFF; } } public void Repeat(int length, int distance) { if ((windowFilled += length) > 32768) { throw new InvalidOperationException("Window full"); } int num = (windowEnd - distance) & 0x7FFF; int num2 = 32768 - length; if (num <= num2 && windowEnd < num2) { if (length <= distance) { Array.Copy(window, num, window, windowEnd, length); windowEnd += length; } else { while (length-- > 0) { window[windowEnd++] = window[num++]; } } } else { SlowRepeat(num, length, distance); } } public int CopyStored(StreamManipulator input, int length) { length = Math.Min(Math.Min(length, 32768 - windowFilled), input.AvailableBytes); int num = 32768 - windowEnd; int num2; if (length > num) { num2 = input.CopyBytes(window, windowEnd, num); if (num2 == num) { num2 += input.CopyBytes(window, 0, length - num); } } else { num2 = input.CopyBytes(window, windowEnd, length); } windowEnd = (windowEnd + num2) & 0x7FFF; windowFilled += num2; return num2; } public void CopyDict(byte[] dictionary, int offset, int length) { if (dictionary == null) { throw new ArgumentNullException("dictionary"); } if (windowFilled > 0) { throw new InvalidOperationException(); } if (length > 32768) { offset += length - 32768; length = 32768; } Array.Copy(dictionary, offset, window, 0, length); windowEnd = length & 0x7FFF; } public int GetFreeSpace() { return 32768 - windowFilled; } public int GetAvailable() { return windowFilled; } public int CopyOutput(byte[] output, int offset, int len) { int num = windowEnd; if (len > windowFilled) { len = windowFilled; } else { num = (windowEnd - windowFilled + len) & 0x7FFF; } int num2 = len; int num3 = len - num; if (num3 > 0) { Array.Copy(window, 32768 - num3, output, offset, num3); offset += num3; len = num; } Array.Copy(window, num - len, output, offset, len); windowFilled -= num2; if (windowFilled < 0) { throw new InvalidOperationException(); } return num2; } public void Reset() { windowFilled = (windowEnd = 0); } } public class StreamManipulator { private byte[] window_; private int windowStart_; private int windowEnd_; private uint buffer_; private int bitsInBuffer_; public int AvailableBits => bitsInBuffer_; public int AvailableBytes => windowEnd_ - windowStart_ + (bitsInBuffer_ >> 3); public bool IsNeedingInput => windowStart_ == windowEnd_; public int PeekBits(int bitCount) { if (bitsInBuffer_ < bitCount) { if (windowStart_ == windowEnd_) { return -1; } buffer_ |= (uint)(((window_[windowStart_++] & 0xFF) | ((window_[windowStart_++] & 0xFF) << 8)) << bitsInBuffer_); bitsInBuffer_ += 16; } return (int)(buffer_ & ((1 << bitCount) - 1)); } public bool TryGetBits(int bitCount, ref int output, int outputOffset = 0) { int num = PeekBits(bitCount); if (num < 0) { return false; } output = num + outputOffset; DropBits(bitCount); return true; } public bool TryGetBits(int bitCount, ref byte[] array, int index) { int num = PeekBits(bitCount); if (num < 0) { return false; } array[index] = (byte)num; DropBits(bitCount); return true; } public void DropBits(int bitCount) { buffer_ >>= bitCount; bitsInBuffer_ -= bitCount; } public int GetBits(int bitCount) { int num = PeekBits(bitCount); if (num >= 0) { DropBits(bitCount); } return num; } public void SkipToByteBoundary() { buffer_ >>= bitsInBuffer_ & 7; bitsInBuffer_ &= -8; } public int CopyBytes(byte[] output, int offset, int length) { if (length < 0) { throw new ArgumentOutOfRangeException("length"); } if ((bitsInBuffer_ & 7) != 0) { throw new InvalidOperationException("Bit buffer is not byte aligned!"); } int num = 0; while (bitsInBuffer_ > 0 && length > 0) { output[offset++] = (byte)buffer_; buffer_ >>= 8; bitsInBuffer_ -= 8; length--; num++; } if (length == 0) { return num; } int num2 = windowEnd_ - windowStart_; if (length > num2) { length = num2; } Array.Copy(window_, windowStart_, output, offset, length); windowStart_ += length; if (((windowStart_ - windowEnd_) & 1) != 0) { buffer_ = (uint)(window_[windowStart_++] & 0xFF); bitsInBuffer_ = 8; } return num + length; } public void Reset() { buffer_ = 0u; windowStart_ = (windowEnd_ = (bitsInBuffer_ = 0)); } public void SetInput(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "Cannot be negative"); } if (count < 0) { throw new ArgumentOutOfRangeException("count", "Cannot be negative"); } if (windowStart_ < windowEnd_) { throw new InvalidOperationException("Old input was not completely processed"); } int num = offset + count; if (offset > num || num > buffer.Length) { throw new ArgumentOutOfRangeException("count"); } if ((count & 1) != 0) { buffer_ |= (uint)((buffer[offset++] & 0xFF) << bitsInBuffer_); bitsInBuffer_ += 8; } window_ = buffer; windowStart_ = offset; windowEnd_ = num; } } } namespace ICSharpCode.SharpZipLib.Tar { [Serializable] public class InvalidHeaderException : TarException { public InvalidHeaderException() { } public InvalidHeaderException(string message) : base(message) { } public InvalidHeaderException(string message, Exception exception) : base(message, exception) { } protected InvalidHeaderException(SerializationInfo info, StreamingContext context) : base(info, context) { } } public delegate void ProgressMessageHandler(TarArchive archive, TarEntry entry, string message); public class TarArchive : IDisposable { 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; public bool AsciiTranslate { get { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } return asciiTranslate; } set { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } asciiTranslate = value; } } public string PathPrefix { get { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } return pathPrefix; } set { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } pathPrefix = value; } } public string RootPath { get { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } return rootPath; } set { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } rootPath = value.ToTarArchivePath().TrimEnd(new char[1] { '/' }); } } public bool ApplyUserInfoOverrides { get { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } return applyUserInfoOverrides; } set { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } applyUserInfoOverrides = value; } } public int UserId { get { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } return userId; } } public string UserName { get { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } return userName; } } public int GroupId { get { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } return groupId; } } public string GroupName { get { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } return groupName; } } public int RecordSize { get { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } if (tarIn != null) { return tarIn.RecordSize; } if (tarOut != null) { return tarOut.RecordSize; } return 10240; } } public bool IsStreamOwner { set { if (tarIn != null) { tarIn.IsStreamOwner = value; } else { tarOut.IsStreamOwner = value; } } } public event ProgressMessageHandler ProgressMessageEvent; protected virtual void OnProgressMessageEvent(TarEntry entry, string message) { this.ProgressMessageEvent?.Invoke(this, entry, message); } protected TarArchive() { } protected TarArchive(TarInputStream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } tarIn = stream; } protected TarArchive(TarOutputStream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } tarOut = stream; } [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); } public static TarArchive CreateInputTarArchive(Stream inputStream, Encoding nameEncoding) { if (inputStream == null) { throw new ArgumentNullException("inputStream"); } if (inputStream is TarInputStream stream) { return new TarArchive(stream); } return CreateInputTarArchive(inputStream, 20, nameEncoding); } [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); } public static TarArchive CreateInputTarArchive(Stream inputStream, int blockFactor, Encoding nameEncoding) { if (inputStream == null) { throw new ArgumentNullException("inputStream"); } if (inputStream is TarInputStream) { throw new ArgumentException("TarInputStream not valid"); } return new TarArchive(new TarInputStream(inputStream, blockFactor, nameEncoding)); } public static TarArchive CreateOutputTarArchive(Stream outputStream, Encoding nameEncoding) { if (outputStream == null) { throw new ArgumentNullException("outputStream"); } if (outputStream is TarOutputStream stream) { return new TarArchive(stream); } return CreateOutputTarArchive(outputStream, 20, nameEncoding); } public static TarArchive CreateOutputTarArchive(Stream outputStream) { return CreateOutputTarArchive(outputStream, null); } public static TarArchive CreateOutputTarArchive(Stream outputStream, int blockFactor) { return CreateOutputTarArchive(outputStream, blockFactor, null); } public static TarArchive CreateOutputTarArchive(Stream outputStream, int blockFactor, Encoding nameEncoding) { if (outputStream == null) { throw new ArgumentNullException("outputStream"); } if (outputStream is TarOutputStream) { throw new ArgumentException("TarOutputStream is not valid"); } return new TarArchive(new TarOutputStream(outputStream, blockFactor, nameEncoding)); } public void SetKeepOldFiles(bool keepExistingFiles) { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } keepOldFiles = keepExistingFiles; } [Obsolete("Use the AsciiTranslate property")] public void SetAsciiTranslation(bool translateAsciiFiles) { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } asciiTranslate = translateAsciiFiles; } 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; } [Obsolete("Use Close instead")] public void CloseArchive() { Close(); } public void ListContents() { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } while (true) { TarEntry nextEntry = tarIn.GetNextEntry(); if (nextEntry != null) { OnProgressMessageEvent(nextEntry, null); continue; } break; } } public void ExtractContents(string destinationDirectory) { ExtractContents(destinationDirectory, allowParentTraversal: false); } public void ExtractContents(string destinationDirectory, bool allowParentTraversal) { if (isDisposed) { throw new ObjectDisposedException("TarArchive"); } string destDir = Path.GetFullPath(destinationDirectory).TrimEnd('/', '\\'); while (true) { TarEntry nextEntry = tarIn.GetNextEntry(); if (nextEntry != null) { if (nextEntry.TarHeader.TypeFlag != 49 && nextEntry.TarHeader.TypeFlag != 50) { ExtractEntry(destDir, nextEntry, allowParentTraversal); } continue; } break; } } private void ExtractEntry(string destDir, TarEntry entry, bool allowParentTraversal) { OnProgressMessageEvent(entry, null); string text = entry.Name; if (Path.IsPathRooted(text)) { text = text.Substring(Path.GetPathRoot(text).Length); } text = text.Replace('/', Path.DirectorySeparatorChar); string text2 = Path.Combine(destDir, text); string text3 = Path.GetDirectoryName(Path.GetFullPath(text2)) ?? ""; bool flag = entry.IsDirectory && entry.Name == ""; if (!allowParentTraversal && !flag && !text3.StartsWith(destDir, StringComparison.InvariantCultureIgnoreCase)) { throw new InvalidNameException("Parent traversal in paths is not allowed"); } if (entry.IsDirectory) { EnsureDirectoryExists(text2); return; } EnsureDirectoryExists(Path.GetDirectoryName(text2)); bool flag2 = true; FileInfo fileInfo = new FileInfo(text2); if (fileInfo.Exists) { if (keepOldFiles) { OnProgressMessageEvent(entry, "Destination file already exists"); flag2 = false; } else if ((fileInfo.Attributes & FileAttributes.ReadOnly) != FileAttributes.None) { OnProgressMessageEvent(entry, "Destination file already exists, and is read-only"); flag2 = false; } } if (!flag2) { return; } using FileStream outputStream = File.Create(text2); if (asciiTranslate) { ExtractAndTranslateEntry(text2, outputStream); } else { tarIn.CopyEntryContents(outputStream); } } private void ExtractAndTranslateEntry(string destFile, Stream outputStream) { if (!IsBinary(destFile)) { using (StreamWriter streamWriter = new StreamWriter(outputStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), 1024, leaveOpen: true)) { byte[] array = new byte[32768]; while (true) { int num = tarIn.Read(array, 0, array.Length); if (num <= 0) { break; } int num2 = 0; for (int i = 0; i < num; i++) { if (array[i] == 10) { string value = Encoding.ASCII.GetString(array, num2, i - num2); streamWriter.WriteLine(value); num2 = i + 1; } } } return; } } tarIn.CopyEntryContents(outputStream); } public void WriteEntry(TarEntry sourceEntry, bool recurse) { if (sourceEntry == null) { throw new ArgumentNullException("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(); } } } private void WriteEntryCore(TarEntry sourceEntry, bool recurse) { string text = null; string text2 = sourceEntry.File; TarEntry tarEntry = (TarEntry)sourceEntry.Clone(); if (applyUserInfoOverrides) { tarEntry.GroupId = groupId; tarEntry.GroupName = groupName; tarEntry.UserId = userId; tarEntry.UserName = userName; } OnProgressMessageEvent(tarEntry, null); if (asciiTranslate && !tarEntry.IsDirectory && !IsBinary(text2)) { text = PathUtils.GetTempFileName(); using (StreamReader streamReader = File.OpenText(text2)) { using Stream stream = File.Create(text); while (true) { string text3 = streamReader.ReadLine(); if (text3 == null) { break; } byte[] bytes = Encoding.ASCII.GetBytes(text3); stream.Write(bytes, 0, bytes.Length); stream.WriteByte(10); } stream.Flush(); } tarEntry.Size = new FileInfo(text).Length; text2 = text; } string text4 = null; if (!string.IsNullOrEmpty(rootPath) && tarEntry.Name.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) { text4 = tarEntry.Name.Substring(rootPath.Length + 1); } if (pathPrefix != null) { text4 = ((text4 == null) ? (pathPrefix + "/" + tarEntry.Name) : (pathPrefix + "/" + text4)); } if (text4 != null) { tarEntry.Name = text4; } tarOut.PutNextEntry(tarEntry); if (tarEntry.IsDirectory) { if (recurse) { TarEntry[] directoryEntries = tarEntry.GetDirectoryEntries(); for (int i = 0; i < directoryEntries.Length; i++) { WriteEntryCore(directoryEntries[i], recurse); } } return; } using (Stream stream2 = File.OpenRead(text2)) { byte[] array = new byte[32768]; while (true) { int num = stream2.Read(array, 0, array.Length); if (num <= 0) { break; } tarOut.Write(array, 0, num); } } if (!string.IsNullOrEmpty(text)) { File.Delete(text); } tarOut.CloseEntry(); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (isDisposed) { return; } isDisposed = true; if (disposing) { if (tarOut != null) { tarOut.Flush(); tarOut.Dispose(); } if (tarIn != null) { tarIn.Dispose(); } } } public virtual void Close() { Dispose(disposing: true); } ~TarArchive() { Dispose(disposing: false); } private static void EnsureDirectoryExists(string directoryName) { if (!Directory.Exists(directoryName)) { try { Directory.CreateDirectory(directoryName); } catch (Exception ex) { throw new TarException("Exception creating directory '" + directoryName + "', " + ex.Message, ex); } } } private static bool IsBinary(string filename) { using (FileStream fileStream = File.OpenRead(filename)) { int num = Math.Min(4096, (int)fileStream.Length); byte[] array = new byte[num]; int num2 = fileStream.Read(array, 0, num); for (int i = 0; i < num2; i++) { byte b = array[i]; if (b < 8 || (b > 13 && b < 32) || b == byte.MaxValue) { return true; } } } return false; } } public class TarBuffer { public const int BlockSize = 512; public const int DefaultBlockFactor = 20; public const int DefaultRecordSize = 10240; private Stream inputStream; private Stream outputStream; private byte[] recordBuffer; private int currentBlockIndex; private int currentRecordIndex; private int recordSize = 10240; private int blockFactor = 20; public int RecordSize => recordSize; public int BlockFactor => blockFactor; public int CurrentBlock => currentBlockIndex; public bool IsStreamOwner { get; set; } = true; public int CurrentRecord => currentRecordIndex; [Obsolete("Use RecordSize property instead")] public int GetRecordSize() { return recordSize; } [Obsolete("Use BlockFactor property instead")] public int GetBlockFactor() { return blockFactor; } protected TarBuffer() { } public static TarBuffer CreateInputTarBuffer(Stream inputStream) { if (inputStream == null) { throw new ArgumentNullException("inputStream"); } return CreateInputTarBuffer(inputStream, 20); } public static TarBuffer CreateInputTarBuffer(Stream inputStream, int blockFactor) { if (inputStream == null) { throw new ArgumentNullException("inputStream"); } if (blockFactor <= 0) { throw new ArgumentOutOfRangeException("blockFactor", "Factor cannot be negative"); } TarBuffer tarBuffer = new TarBuffer(); tarBuffer.inputStream = inputStream; tarBuffer.outputStream = null; tarBuffer.Initialize(blockFactor); return tarBuffer; } public static TarBuffer CreateOutputTarBuffer(Stream outputStream) { if (outputStream == null) { throw new ArgumentNullException("outputStream"); } return CreateOutputTarBuffer(outputStream, 20); } public static TarBuffer CreateOutputTarBuffer(Stream outputStream, int blockFactor) { if (outputStream == null) { throw new ArgumentNullException("outputStream"); } if (blockFactor <= 0) { throw new ArgumentOutOfRangeException("blockFactor", "Factor cannot be negative"); } TarBuffer tarBuffer = new TarBuffer(); tarBuffer.inputStream = null; tarBuffer.outputStream = outputStream; tarBuffer.Initialize(blockFactor); return tarBuffer; } private void Initialize(int archiveBlockFactor) { blockFactor = archiveBlockFactor; recordSize = archiveBlockFactor * 512; recordBuffer = ArrayPool.Shared.Rent(RecordSize); if (inputStream != null) { currentRecordIndex = -1; currentBlockIndex = BlockFactor; } else { currentRecordIndex = 0; currentBlockIndex = 0; } } [Obsolete("Use IsEndOfArchiveBlock instead")] public bool IsEOFBlock(byte[] block) { if (block == null) { throw new ArgumentNullException("block"); } if (block.Length != 512) { throw new ArgumentException("block length is invalid"); } for (int i = 0; i < 512; i++) { if (block[i] != 0) { return false; } } return true; } public static bool IsEndOfArchiveBlock(byte[] block) { if (block == null) { throw new ArgumentNullException("block"); } if (block.Length != 512) { throw new ArgumentException("block length is invalid"); } for (int i = 0; i < 512; i++) { if (block[i] != 0) { return false; } } return true; } public void SkipBlock() { SkipBlockAsync(CancellationToken.None, isAsync: false).GetAwaiter().GetResult(); } public Task SkipBlockAsync(CancellationToken ct) { return SkipBlockAsync(ct, isAsync: true).AsTask(); } private async ValueTask SkipBlockAsync(CancellationToken ct, bool isAsync) { if (inputStream == null) { throw new TarException("no input stream defined"); } if (currentBlockIndex >= BlockFactor && !(await ReadRecordAsync(ct, isAsync))) { throw new TarException("Failed to read a record"); } currentBlockIndex++; } public byte[] ReadBlock() { if (inputStream == null) { throw new TarException("TarBuffer.ReadBlock - no input stream defined"); } if (currentBlockIndex >= BlockFactor && !ReadRecordAsync(CancellationToken.None, isAsync: false).GetAwaiter().GetResult()) { throw new TarException("Failed to read a record"); } byte[] array = new byte[512]; Array.Copy(recordBuffer, currentBlockIndex * 512, array, 0, 512); currentBlockIndex++; return array; } internal async ValueTask ReadBlockIntAsync(byte[] buffer, CancellationToken ct, bool isAsync) { if (buffer.Length != 512) { throw new ArgumentException("BUG: buffer must have length BlockSize"); } if (inputStream == null) { throw new TarException("TarBuffer.ReadBlock - no input stream defined"); } if (currentBlockIndex >= BlockFactor && !(await ReadRecordAsync(ct, isAsync))) { throw new TarException("Failed to read a record"); } Span span = recordBuffer.AsSpan(); span = span.Slice(currentBlockIndex * 512, 512); span.CopyTo(buffer); currentBlockIndex++; } private async ValueTask ReadRecordAsync(CancellationToken ct, bool isAsync) { if (inputStream == null) { throw new TarException("no input stream defined"); } currentBlockIndex = 0; int offset = 0; int bytesNeeded = RecordSize; while (bytesNeeded > 0) { int num = ((!isAsync) ? inputStream.Read(recordBuffer, offset, bytesNeeded) : (await inputStream.ReadAsync(recordBuffer, offset, bytesNeeded, ct))); long num2 = num; if (num2 <= 0) { break; } offset += (int)num2; bytesNeeded -= (int)num2; } currentRecordIndex++; return true; } [Obsolete("Use CurrentBlock property instead")] public int GetCurrentBlockNum() { return currentBlockIndex; } [Obsolete("Use CurrentRecord property instead")] public int GetCurrentRecordNum() { return currentRecordIndex; } public ValueTask WriteBlockAsync(byte[] block, CancellationToken ct) { return WriteBlockAsync(block, 0, ct); } public void WriteBlock(byte[] block) { WriteBlock(block, 0); } public ValueTask WriteBlockAsync(byte[] buffer, int offset, CancellationToken ct) { return WriteBlockAsync(buffer, offset, ct, isAsync: true); } public void WriteBlock(byte[] buffer, int offset) { WriteBlockAsync(buffer, offset, CancellationToken.None, isAsync: false).GetAwaiter().GetResult(); } internal async ValueTask WriteBlockAsync(byte[] buffer, int offset, CancellationToken ct, bool isAsync) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (outputStream == null) { throw new TarException("TarBuffer.WriteBlock - no output stream defined"); } if (offset < 0 || offset >= buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } if (offset + 512 > buffer.Length) { throw new TarException($"TarBuffer.WriteBlock - record has length '{buffer.Length}' with offset '{offset}' which is less than the record size of '{recordSize}'"); } if (currentBlockIndex >= BlockFactor) { await WriteRecordAsync(CancellationToken.None, isAsync); } Array.Copy(buffer, offset, recordBuffer, currentBlockIndex * 512, 512); currentBlockIndex++; } private async ValueTask WriteRecordAsync(CancellationToken ct, bool isAsync) { if (outputStream == null) { throw new TarException("TarBuffer.WriteRecord no output stream defined"); } if (isAsync) { await outputStream.WriteAsync(recordBuffer, 0, RecordSize, ct); await outputStream.FlushAsync(ct); } else { outputStream.Write(recordBuffer, 0, RecordSize); outputStream.Flush(); } currentBlockIndex = 0; currentRecordIndex++; } private async ValueTask WriteFinalRecordAsync(CancellationToken ct, bool isAsync) { if (outputStream == null) { throw new TarException("TarBuffer.WriteFinalRecord no output stream defined"); } if (currentBlockIndex > 0) { int num = currentBlockIndex * 512; Array.Clear(recordBuffer, num, RecordSize - num); await WriteRecordAsync(ct, isAsync); } if (isAsync) { await outputStream.FlushAsync(ct); } else { outputStream.Flush(); } } public void Close() { CloseAsync(CancellationToken.None, isAsync: false).GetAwaiter().GetResult(); } public Task CloseAsync(CancellationToken ct) { return CloseAsync(ct, isAsync: true).AsTask(); } private async ValueTask CloseAsync(CancellationToken ct, bool isAsync) { if (outputStream != null) { await WriteFinalRecordAsync(ct, isAsync); if (IsStreamOwner) { if (isAsync) { outputStream.Dispose(); } else { outputStream.Dispose(); } } outputStream = null; } else if (inputStream != null) { if (IsStreamOwner) { if (isAsync) { inputStream.Dispose(); } else { inputStream.Dispose(); } } inputStream = null; } ArrayPool.Shared.Return(recordBuffer); } } public class TarEntry { private string file; private TarHeader header; public TarHeader TarHeader => header; public string Name { get { return header.Name; } set { header.Name = value; } } public int UserId { get { return header.UserId; } set { header.UserId = value; } } public int GroupId { get { return header.GroupId; } set { header.GroupId = value; } } public string UserName { get { return header.UserName; } set { header.UserName = value; } } public string GroupName { get { return header.GroupName; } set { header.GroupName = value; } } public DateTime ModTime { get { return header.ModTime; } set { header.ModTime = value; } } public string File => file; public long Size { get { return header.Size; } set { header.Size = value; } } public bool IsDirectory { get { if (file != null) { return Directory.Exists(file); } if (header != null && (header.TypeFlag == 53 || Name.EndsWith("/", StringComparison.Ordinal))) { return true; } return false; } } private TarEntry() { header = new TarHeader(); } [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] public TarEntry(byte[] headerBuffer) : this(headerBuffer, null) { } public TarEntry(byte[] headerBuffer, Encoding nameEncoding) { header = new TarHeader(); header.ParseBuffer(headerBuffer, nameEncoding); } public TarEntry(TarHeader header) { if (header == null) { throw new ArgumentNullException("header"); } this.header = (TarHeader)header.Clone(); } public object Clone() { return new TarEntry { file = file, header = (TarHeader)header.Clone(), Name = Name }; } public static TarEntry CreateTarEntry(string name) { TarEntry tarEntry = new TarEntry(); tarEntry.NameTarHeader(name); return tarEntry; } public static TarEntry CreateEntryFromFile(string fileName) { TarEntry tarEntry = new TarEntry(); tarEntry.GetFileTarHeader(tarEntry.header, fileName); return tarEntry; } public override bool Equals(object obj) { if (obj is TarEntry tarEntry) { return Name.Equals(tarEntry.Name); } return false; } public override int GetHashCode() { return Name.GetHashCode(); } public bool IsDescendent(TarEntry toTest) { if (toTest == null) { throw new ArgumentNullException("toTest"); } return toTest.Name.StartsWith(Name, StringComparison.Ordinal); } public void SetIds(int userId, int groupId) { UserId = userId; GroupId = groupId; } public void SetNames(string userName, string groupName) { UserName = userName; GroupName = groupName; } public void GetFileTarHeader(TarHeader header, string file) { if (header == null) { throw new ArgumentNullException("header"); } if (file == null) { throw new ArgumentNullException("file"); } this.file = file; string text = file; if (text.IndexOf(Directory.GetCurrentDirectory(), StringComparison.Ordinal) == 0) { text = text.Substring(Directory.GetCurrentDirectory().Length); } text = text.ToTarArchivePath(); header.LinkName = string.Empty; header.Name = text; if (Directory.Exists(file)) { header.Mode = 1003; header.TypeFlag = 53; if (header.Name.Length == 0 || header.Name[header.Name.Length - 1] != '/') { header.Name += "/"; } header.Size = 0L; } else { header.Mode = 33216; header.TypeFlag = 48; 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; } public TarEntry[] GetDirectoryEntries() { if (file == null || !Directory.Exists(file)) { return Empty.Array(); } string[] fileSystemEntries = Directory.GetFileSystemEntries(file); TarEntry[] array = new TarEntry[fileSystemEntries.Length]; for (int i = 0; i < fileSystemEntries.Length; i++) { array[i] = CreateEntryFromFile(fileSystemEntries[i]); } return array; } [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] public void WriteEntryHeader(byte[] outBuffer) { WriteEntryHeader(outBuffer, null); } public void WriteEntryHeader(byte[] outBuffer, Encoding nameEncoding) { header.WriteHeader(outBuffer, nameEncoding); } [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] public static void AdjustEntryName(byte[] buffer, string newName) { AdjustEntryName(buffer, newName, null); } public static void AdjustEntryName(byte[] buffer, string newName, Encoding nameEncoding) { TarHeader.GetNameBytes(newName, buffer, 0, 100, nameEncoding); } public void NameTarHeader(string name) { if (name == null) { throw new ArgumentNullException("name"); } bool flag = name.EndsWith("/", StringComparison.Ordinal); header.Name = name; header.Mode = (flag ? 1003 : 33216); header.UserId = 0; header.GroupId = 0; header.Size = 0L; header.ModTime = DateTime.UtcNow; header.TypeFlag = (byte)(flag ? 53 : 48); header.LinkName = string.Empty; header.UserName = string.Empty; header.GroupName = string.Empty; header.DevMajor = 0; header.DevMinor = 0; } } [Serializable] public class TarException : SharpZipBaseException { public TarException() { } public TarException(string message) : base(message) { } public TarException(string message, Exception innerException) : base(message, innerException) { } protected TarException(SerializationInfo info, StreamingContext context) : base(info, context) { } } 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; private int currHeaderLength; private int currHeaderRead; private static readonly byte[] StateNext = new byte[3] { 32, 61, 10 }; public Dictionary Headers => headers; public TarExtendedHeaderReader() { ResetBuffers(); } public void Read(byte[] buffer, int length) { for (int i = 0; i < length; i++) { byte b = buffer[i]; if ((state == 2) ? (currHeaderRead == currHeaderLength - 1) : (b == StateNext[state])) { Flush(); headerParts[state] = sb.ToString(); sb.Clear(); if (++state == 3) { if (!headers.ContainsKey(headerParts[1])) { headers.Add(headerParts[1], headerParts[2]); } headerParts = new string[3]; currHeaderLength = 0; currHeaderRead = 0; state = 0; } else { currHeaderRead++; } if (state == 2 && int.TryParse(headerParts[0], out var result)) { currHeaderLength = result; } } else { byteBuffer[bbIndex++] = b; currHeaderRead++; if (bbIndex == 4) { Flush(); } } } } private void Flush() { decoder.Convert(byteBuffer, 0, bbIndex, charBuffer, 0, 4, flush: false, out var _, out var charsUsed, out var _); sb.Append(charBuffer, 0, charsUsed); ResetBuffers(); } private void ResetBuffers() { charBuffer = new char[4]; byteBuffer = new byte[4]; bbIndex = 0; } } public class TarHeader { public const int NAMELEN = 100; public const int MODELEN = 8; public const int UIDLEN = 8; public const int GIDLEN = 8; public const int CHKSUMLEN = 8; public const int CHKSUMOFS = 148; public const int SIZELEN = 12; public const int MAGICLEN = 6; public const int VERSIONLEN = 2; public const int MODTIMELEN = 12; public const int UNAMELEN = 32; public const int GNAMELEN = 32; public const int DEVLEN = 8; public const int PREFIXLEN = 155; public const byte LF_OLDNORM = 0; public const byte LF_NORMAL = 48; public const byte LF_LINK = 49; public const byte LF_SYMLINK = 50; public const byte LF_CHR = 51; public const byte LF_BLK = 52; public const byte LF_DIR = 53; public const byte LF_FIFO = 54; public const byte LF_CONTIG = 55; public const byte LF_GHDR = 103; public const byte LF_XHDR = 120; public const byte LF_ACL = 65; public const byte LF_GNU_DUMPDIR = 68; public const byte LF_EXTATTR = 69; public const byte LF_META = 73; public const byte LF_GNU_LONGLINK = 75; public const byte LF_GNU_LONGNAME = 76; public const byte LF_GNU_MULTIVOL = 77; public const byte LF_GNU_NAMES = 78; public const byte LF_GNU_SPARSE = 83; public const byte LF_GNU_VOLHDR = 86; public const string TMAGIC = "ustar"; public const string GNU_TMAGIC = "ustar "; private const long timeConversionFactor = 10000000L; private static readonly DateTime dateTime1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0); 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; internal static int userIdAsSet; internal static int groupIdAsSet; internal static string userNameAsSet; internal static string groupNameAsSet = "None"; internal static int defaultUserId; internal static int defaultGroupId; internal static string defaultGroupName = "None"; internal static string defaultUser; public string Name { get { return name; } set { if (value == null) { throw new ArgumentNullException("value"); } name = value; } } public int Mode { get { return mode; } set { mode = value; } } public int UserId { get { return userId; } set { userId = value; } } public int GroupId { get { return groupId; } set { groupId = value; } } public long Size { get { return size; } set { if (value < 0) { throw new ArgumentOutOfRangeException("value", "Cannot be less than zero"); } size = value; } } public DateTime ModTime { get { return modTime; } set { if (value < dateTime1970) { throw new ArgumentOutOfRangeException("value", "ModTime cannot be before Jan 1st 1970"); } modTime = new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second); } } public int Checksum => checksum; public bool IsChecksumValid => isChecksumValid; public byte TypeFlag { get { return typeFlag; } set { typeFlag = value; } } public string LinkName { get { return linkName; } set { if (value == null) { throw new ArgumentNullException("value"); } linkName = value; } } public string Magic { get { return magic; } set { if (value == null) { throw new ArgumentNullException("value"); } magic = value; } } public string Version { get { return version; } set { if (value == null) { throw new ArgumentNullException("value"); } version = value; } } public string UserName { get { return userName; } set { if (value != null) { userName = value.Substring(0, Math.Min(32, value.Length)); return; } string text = "user"; if (text.Length > 32) { text = text.Substring(0, 32); } userName = text; } } public string GroupName { get { return groupName; } set { if (value == null) { groupName = "None"; } else { groupName = value; } } } public int DevMajor { get { return devMajor; } set { devMajor = value; } } public int DevMinor { get { return devMinor; } set { devMinor = value; } } public TarHeader() { Magic = "ustar"; Version = " "; Name = ""; LinkName = ""; UserId = defaultUserId; GroupId = defaultGroupId; UserName = defaultUser; GroupName = defaultGroupName; Size = 0L; } [Obsolete("Use the Name property instead", true)] public string GetName() { return name; } public object Clone() { return MemberwiseClone(); } public void ParseBuffer(byte[] header, Encoding nameEncoding) { if (header == null) { throw new ArgumentNullException("header"); } int num = 0; Span span = header.AsSpan(); name = ParseName(span.Slice(num, 100), nameEncoding); num += 100; mode = (int)ParseOctal(header, num, 8); num += 8; UserId = (int)ParseOctal(header, num, 8); num += 8; GroupId = (int)ParseOctal(header, num, 8); num += 8; Size = ParseBinaryOrOctal(header, num, 12); num += 12; ModTime = GetDateTimeFromCTime(ParseOctal(header, num, 12)); num += 12; checksum = (int)ParseOctal(header, num, 8); num += 8; TypeFlag = header[num++]; LinkName = ParseName(span.Slice(num, 100), nameEncoding); num += 100; Magic = ParseName(span.Slice(num, 6), nameEncoding); num += 6; if (Magic == "ustar") { Version = ParseName(span.Slice(num, 2), nameEncoding); num += 2; UserName = ParseName(span.Slice(num, 32), nameEncoding); num += 32; GroupName = ParseName(span.Slice(num, 32), nameEncoding); num += 32; DevMajor = (int)ParseOctal(header, num, 8); num += 8; DevMinor = (int)ParseOctal(header, num, 8); num += 8; string text = ParseName(span.Slice(num, 155), nameEncoding); if (!string.IsNullOrEmpty(text)) { Name = text + "/" + Name; } } isChecksumValid = Checksum == MakeCheckSum(header); } [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] public void ParseBuffer(byte[] header) { ParseBuffer(header, null); } [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] public void WriteHeader(byte[] outBuffer) { WriteHeader(outBuffer, null); } public void WriteHeader(byte[] outBuffer, Encoding nameEncoding) { if (outBuffer == null) { throw new ArgumentNullException("outBuffer"); } int offset = 0; offset = GetNameBytes(Name, outBuffer, offset, 100, nameEncoding); offset = GetOctalBytes(mode, outBuffer, offset, 8); offset = GetOctalBytes(UserId, outBuffer, offset, 8); offset = GetOctalBytes(GroupId, outBuffer, offset, 8); offset = GetBinaryOrOctalBytes(Size, outBuffer, offset, 12); offset = GetOctalBytes(GetCTime(ModTime), outBuffer, offset, 12); int offset2 = offset; for (int i = 0; i < 8; i++) { outBuffer[offset++] = 32; } outBuffer[offset++] = TypeFlag; offset = GetNameBytes(LinkName, outBuffer, offset, 100, nameEncoding); offset = GetAsciiBytes(Magic, 0, outBuffer, offset, 6, nameEncoding); offset = GetNameBytes(Version, outBuffer, offset, 2, nameEncoding); offset = GetNameBytes(UserName, outBuffer, offset, 32, nameEncoding); offset = GetNameBytes(GroupName, outBuffer, offset, 32, nameEncoding); if (TypeFlag == 51 || TypeFlag == 52) { offset = GetOctalBytes(DevMajor, outBuffer, offset, 8); offset = GetOctalBytes(DevMinor, outBuffer, offset, 8); } while (offset < outBuffer.Length) { outBuffer[offset++] = 0; } checksum = ComputeCheckSum(outBuffer); GetCheckSumOctalBytes(checksum, outBuffer, offset2, 8); isChecksumValid = true; } public override int GetHashCode() { return Name.GetHashCode(); } public override bool Equals(object obj) { if (obj is TarHeader tarHeader) { return name == tarHeader.name && mode == tarHeader.mode && UserId == tarHeader.UserId && GroupId == tarHeader.GroupId && Size == tarHeader.Size && ModTime == tarHeader.ModTime && Checksum == tarHeader.Checksum && TypeFlag == tarHeader.TypeFlag && LinkName == tarHeader.LinkName && Magic == tarHeader.Magic && Version == tarHeader.Version && UserName == tarHeader.UserName && GroupName == tarHeader.GroupName && DevMajor == tarHeader.DevMajor && DevMinor == tarHeader.DevMinor; } return false; } internal static void SetValueDefaults(int userId, string userName, int groupId, string groupName) { defaultUserId = (userIdAsSet = userId); defaultUser = (userNameAsSet = userName); defaultGroupId = (groupIdAsSet = groupId); defaultGroupName = (groupNameAsSet = groupName); } internal static void RestoreSetValues() { defaultUserId = userIdAsSet; defaultUser = userNameAsSet; defaultGroupId = groupIdAsSet; defaultGroupName = groupNameAsSet; } private static long ParseBinaryOrOctal(byte[] header, int offset, int length) { if (header[offset] >= 128) { long num = 0L; for (int i = length - 8; i < length; i++) { num = (num << 8) | header[offset + i]; } return num; } return ParseOctal(header, offset, length); } public static long ParseOctal(byte[] header, int offset, int length) { if (header == null) { throw new ArgumentNullException("header"); } long num = 0L; bool flag = true; int num2 = offset + length; for (int i = offset; i < num2 && header[i] != 0; i++) { if (header[i] == 32 || header[i] == 48) { if (flag) { continue; } if (header[i] == 32) { break; } } flag = false; num = (num << 3) + (header[i] - 48); } return num; } [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] public static string ParseName(byte[] header, int offset, int length) { return ParseName(header.AsSpan().Slice(offset, length), null); } public static string ParseName(ReadOnlySpan header, Encoding encoding) { StringBuilder stringBuilder = StringBuilderPool.Instance.Rent(); int num = 0; if (encoding == null) { for (int i = 0; i < header.Length; i++) { byte b = header[i]; if (b == 0) { break; } stringBuilder.Append((char)b); } } else { int num2 = 0; while (num2 < header.Length && header[num2] != 0) { num2++; num++; } string value = encoding.GetString(header.ToArray(), 0, num); stringBuilder.Append(value); } string result = stringBuilder.ToString(); StringBuilderPool.Instance.Return(stringBuilder); return result; } public static int GetNameBytes(StringBuilder name, int nameOffset, byte[] buffer, int bufferOffset, int length) { return GetNameBytes(name.ToString(), nameOffset, buffer, bufferOffset, length, null); } public static int GetNameBytes(string name, int nameOffset, byte[] buffer, int bufferOffset, int length) { return GetNameBytes(name, nameOffset, buffer, bufferOffset, length, null); } public static int GetNameBytes(string name, int nameOffset, byte[] buffer, int bufferOffset, int length, Encoding encoding) { if (name == null) { throw new ArgumentNullException("name"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } int i; if (encoding != null) { ReadOnlySpan readOnlySpan = name.AsSpan().Slice(nameOffset, Math.Min(name.Length - nameOffset, length)); char[] array = ArrayPool.Shared.Rent(readOnlySpan.Length); readOnlySpan.CopyTo(array); int bytes = encoding.GetBytes(array, 0, readOnlySpan.Length, buffer, bufferOffset); ArrayPool.Shared.Return(array); i = Math.Min(bytes, length); } 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; } [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); } public static int GetNameBytes(StringBuilder name, byte[] buffer, int offset, int length, Encoding encoding) { if (name == null) { throw new ArgumentNullException("name"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } return GetNameBytes(name.ToString(), 0, buffer, offset, length, encoding); } [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); } public static int GetNameBytes(string name, byte[] buffer, int offset, int length, Encoding encoding) { if (name == null) { throw new ArgumentNullException("name"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } return GetNameBytes(name, 0, buffer, offset, length, encoding); } [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); } public static int GetAsciiBytes(string toAdd, int nameOffset, byte[] buffer, int bufferOffset, int length, Encoding encoding) { if (toAdd == null) { throw new ArgumentNullException("toAdd"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } int i; if (encoding == null) { for (i = 0; i < length && nameOffset + i < toAdd.Length; i++) { buffer[bufferOffset + i] = (byte)toAdd[nameOffset + i]; } } else { char[] chars = toAdd.ToCharArray(); byte[] bytes = encoding.GetBytes(chars, nameOffset, Math.Min(toAdd.Length - nameOffset, length)); i = Math.Min(bytes.Length, length); Array.Copy(bytes, 0, buffer, bufferOffset, i); } for (; i < length; i++) { buffer[bufferOffset + i] = 0; } return bufferOffset + length; } public static int GetOctalBytes(long value, byte[] buffer, int offset, int length) { if (buffer == null) { throw new ArgumentNullException("buffer"); } int num = length - 1; buffer[offset + num] = 0; num--; if (value > 0) { long num2 = value; while (num >= 0 && num2 > 0) { buffer[offset + num] = (byte)(48 + (byte)(num2 & 7)); num2 >>= 3; num--; } } while (num >= 0) { buffer[offset + num] = 48; num--; } return offset + length; } private static int GetBinaryOrOctalBytes(long value, byte[] buffer, int offset, int length) { if (value > 8589934591L) { for (int num = length - 1; num > 0; num--) { buffer[offset + num] = (byte)value; value >>= 8; } buffer[offset] = 128; return offset + length; } return GetOctalBytes(value, buffer, offset, length); } private static void GetCheckSumOctalBytes(long value, byte[] buffer, int offset, int length) { GetOctalBytes(value, buffer, offset, length - 1); } private static int ComputeCheckSum(byte[] buffer) { int num = 0; for (int i = 0; i < buffer.Length; i++) { num += buffer[i]; } return num; } private static int MakeCheckSum(byte[] buffer) { int num = 0; for (int i = 0; i < 148; i++) { num += buffer[i]; } for (int j = 0; j < 8; j++) { num += 32; } for (int k = 156; k < buffer.Length; k++) { num += buffer[k]; } return num; } private static int GetCTime(DateTime dateTime) { long ticks = dateTime.Ticks; DateTime dateTime2 = dateTime1970; return (int)((ticks - dateTime2.Ticks) / 10000000); } private static DateTime GetDateTimeFromCTime(long ticks) { try { DateTime dateTime = dateTime1970; return new DateTime(dateTime.Ticks + ticks * 10000000); } catch (ArgumentOutOfRangeException) { return dateTime1970; } } } public class TarInputStream : Stream { public interface IEntryFactory { TarEntry CreateEntry(string name); TarEntry CreateEntryFromFile(string fileName); TarEntry CreateEntry(byte[] headerBuffer); } public class EntryFactoryAdapter : IEntryFactory { private Encoding nameEncoding; [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] public EntryFactoryAdapter() { } public EntryFactoryAdapter(Encoding nameEncoding) { this.nameEncoding = nameEncoding; } public TarEntry CreateEntry(string name) { return TarEntry.CreateTarEntry(name); } public TarEntry CreateEntryFromFile(string fileName) { return TarEntry.CreateEntryFromFile(fileName); } public TarEntry CreateEntry(byte[] headerBuffer) { return new TarEntry(headerBuffer, nameEncoding); } } protected bool hasHitEOF; protected long entrySize; protected long entryOffset; protected IMemoryOwner readBuffer; protected TarBuffer tarBuffer; private TarEntry currentEntry; protected IEntryFactory entryFactory; private readonly Stream inputStream; private readonly Encoding encoding; public bool IsStreamOwner { get { return tarBuffer.IsStreamOwner; } set { tarBuffer.IsStreamOwner = value; } } public override bool CanRead => inputStream.CanRead; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length => inputStream.Length; public override long Position { get { return inputStream.Position; } set { throw new NotSupportedException("TarInputStream Seek not supported"); } } public int RecordSize => tarBuffer.RecordSize; public long Available => entrySize - entryOffset; public bool IsMarkSupported => false; [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] public TarInputStream(Stream inputStream) : this(inputStream, 20, null) { } public TarInputStream(Stream inputStream, Encoding nameEncoding) : this(inputStream, 20, nameEncoding) { } [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; } public TarInputStream(Stream inputStream, int blockFactor, Encoding nameEncoding) { this.inputStream = inputStream; tarBuffer = TarBuffer.CreateInputTarBuffer(inputStream, blockFactor); encoding = nameEncoding; } public override void Flush() { inputStream.Flush(); } public override async Task FlushAsync(CancellationToken cancellationToken) { await inputStream.FlushAsync(cancellationToken); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException("TarInputStream Seek not supported"); } public override void SetLength(long value) { throw new NotSupportedException("TarInputStream SetLength not supported"); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException("TarInputStream Write not supported"); } public override void WriteByte(byte value) { throw new NotSupportedException("TarInputStream WriteByte not supported"); } public override int ReadByte() { byte[] array = ArrayPool.Shared.Rent(1); if (Read(array, 0, 1) <= 0) { return -1; } byte result = array[0]; ArrayPool.Shared.Return(array); return result; } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return ReadAsync(buffer.AsMemory().Slice(offset, count), cancellationToken, isAsync: true).AsTask(); } public override int Read(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException("buffer"); } return ReadAsync(buffer.AsMemory().Slice(offset, count), CancellationToken.None, isAsync: false).GetAwaiter().GetResult(); } private async ValueTask ReadAsync(Memory buffer, CancellationToken ct, bool isAsync) { int offset = 0; int totalRead = 0; if (entryOffset >= entrySize) { return 0; } long numToRead = buffer.Length; if (numToRead + entryOffset > entrySize) { numToRead = entrySize - entryOffset; } if (readBuffer != null) { int num = (int)((numToRead > readBuffer.Memory.Length) ? readBuffer.Memory.Length : numToRead); readBuffer.Memory.Slice(0, num).CopyTo(buffer.Slice(offset, num)); if (num >= readBuffer.Memory.Length) { readBuffer.Dispose(); readBuffer = null; } else { int num2 = readBuffer.Memory.Length - num; IMemoryOwner memoryOwner = ExactMemoryPool.Shared.Rent(num2); readBuffer.Memory.Slice(num, num2).CopyTo(memoryOwner.Memory); readBuffer.Dispose(); readBuffer = memoryOwner; } totalRead += num; numToRead -= num; offset += num; } int recLen = 512; byte[] recBuf = ArrayPool.Shared.Rent(recLen); while (numToRead > 0) { await tarBuffer.ReadBlockIntAsync(recBuf, ct, isAsync); int num3 = (int)numToRead; Span span; if (recLen > num3) { span = recBuf.AsSpan(); span = span.Slice(0, num3); span.CopyTo(buffer.Slice(offset, num3).Span); readBuffer?.Dispose(); readBuffer = ExactMemoryPool.Shared.Rent(recLen - num3); span = recBuf.AsSpan(); span = span.Slice(num3, recLen - num3); span.CopyTo(readBuffer.Memory.Span); } else { num3 = recLen; span = recBuf.AsSpan(); span.CopyTo(buffer.Slice(offset, recLen).Span); } totalRead += num3; numToRead -= num3; offset += num3; } ArrayPool.Shared.Return(recBuf); entryOffset += totalRead; return totalRead; } protected override void Dispose(bool disposing) { if (disposing) { tarBuffer.Close(); } } public void SetEntryFactory(IEntryFactory factory) { entryFactory = factory; } [Obsolete("Use RecordSize property instead")] public int GetRecordSize() { return tarBuffer.RecordSize; } private Task SkipAsync(long skipCount, CancellationToken ct) { return SkipAsync(skipCount, ct, isAsync: true).AsTask(); } private void Skip(long skipCount) { SkipAsync(skipCount, CancellationToken.None, isAsync: false).GetAwaiter().GetResult(); } private async ValueTask SkipAsync(long skipCount, CancellationToken ct, bool isAsync) { int length = 8192; using IMemoryOwner skipBuf = ExactMemoryPool.Shared.Rent(length); long num = skipCount; while (num > 0) { int length2 = (int)((num > length) ? length : num); int num2 = await ReadAsync(skipBuf.Memory.Slice(0, length2), ct, isAsync); if (num2 != -1) { num -= num2; continue; } break; } } public void Mark(int markLimit) { } public void Reset() { } public Task GetNextEntryAsync(CancellationToken ct) { return GetNextEntryAsync(ct, isAsync: true).AsTask(); } public TarEntry GetNextEntry() { return GetNextEntryAsync(CancellationToken.None, isAsync: true).GetAwaiter().GetResult(); } private async ValueTask GetNextEntryAsync(CancellationToken ct, bool isAsync) { if (hasHitEOF) { return null; } if (currentEntry != null) { await SkipToNextEntryAsync(ct, isAsync); } byte[] headerBuf = ArrayPool.Shared.Rent(512); await tarBuffer.ReadBlockIntAsync(headerBuf, ct, isAsync); if (TarBuffer.IsEndOfArchiveBlock(headerBuf)) { hasHitEOF = true; await tarBuffer.ReadBlockIntAsync(headerBuf, ct, isAsync); } else { hasHitEOF = false; } if (hasHitEOF) { currentEntry = null; readBuffer?.Dispose(); } else { try { TarHeader tarHeader = new TarHeader(); tarHeader.ParseBuffer(headerBuf, encoding); if (!tarHeader.IsChecksumValid) { throw new TarException("Header checksum is invalid"); } entryOffset = 0L; entrySize = tarHeader.Size; string longName = null; if (tarHeader.TypeFlag == 76) { using IMemoryOwner nameBuffer = ExactMemoryPool.Shared.Rent(512); long numToRead = entrySize; StringBuilder longNameBuilder = StringBuilderPool.Instance.Rent(); while (numToRead > 0) { int length = (int)((numToRead > 512) ? 512 : numToRead); int num = await ReadAsync(nameBuffer.Memory.Slice(0, length), ct, isAsync); if (num == -1) { throw new InvalidHeaderException("Failed to read long name entry"); } longNameBuilder.Append(TarHeader.ParseName(nameBuffer.Memory.Slice(0, num).Span, encoding)); numToRead -= num; } longName = longNameBuilder.ToString(); StringBuilderPool.Instance.Return(longNameBuilder); await SkipToNextEntryAsync(ct, isAsync); await tarBuffer.ReadBlockIntAsync(headerBuf, ct, isAsync); } else if (tarHeader.TypeFlag == 103) { await SkipToNextEntryAsync(ct, isAsync); await tarBuffer.ReadBlockIntAsync(headerBuf, ct, isAsync); } else if (tarHeader.TypeFlag == 120) { byte[] nameBuffer2 = ArrayPool.Shared.Rent(512); long numToRead = entrySize; TarExtendedHeaderReader xhr = new TarExtendedHeaderReader(); while (numToRead > 0) { int length2 = (int)((numToRead > nameBuffer2.Length) ? nameBuffer2.Length : numToRead); int num2 = await ReadAsync(nameBuffer2.AsMemory().Slice(0, length2), ct, isAsync); if (num2 == -1) { throw new InvalidHeaderException("Failed to read long name entry"); } xhr.Read(nameBuffer2, num2); numToRead -= num2; } ArrayPool.Shared.Return(nameBuffer2); if (xhr.Headers.TryGetValue("path", out var value)) { longName = value; } await SkipToNextEntryAsync(ct, isAsync); await tarBuffer.ReadBlockIntAsync(headerBuf, ct, isAsync); } else if (tarHeader.TypeFlag == 86) { await SkipToNextEntryAsync(ct, isAsync); await tarBuffer.ReadBlockIntAsync(headerBuf, ct, isAsync); } else if (tarHeader.TypeFlag != 48 && tarHeader.TypeFlag != 0 && tarHeader.TypeFlag != 49 && tarHeader.TypeFlag != 50 && tarHeader.TypeFlag != 53) { await SkipToNextEntryAsync(ct, isAsync); await tarBuffer.ReadBlockIntAsync(headerBuf, ct, isAsync); } if (entryFactory == null) { currentEntry = new TarEntry(headerBuf, encoding); readBuffer?.Dispose(); if (longName != null) { currentEntry.Name = longName; } } else { currentEntry = entryFactory.CreateEntry(headerBuf); readBuffer?.Dispose(); } entryOffset = 0L; entrySize = currentEntry.Size; } catch (InvalidHeaderException ex) { entrySize = 0L; entryOffset = 0L; currentEntry = null; readBuffer?.Dispose(); throw new InvalidHeaderException($"Bad header in record {tarBuffer.CurrentRecord} block {tarBuffer.CurrentBlock} {ex.Message}"); } } ArrayPool.Shared.Return(headerBuf); return currentEntry; } public Task CopyEntryContentsAsync(Stream outputStream, CancellationToken ct) { return CopyEntryContentsAsync(outputStream, ct, isAsync: true).AsTask(); } public void CopyEntryContents(Stream outputStream) { CopyEntryContentsAsync(outputStream, CancellationToken.None, isAsync: false).GetAwaiter().GetResult(); } private async ValueTask CopyEntryContentsAsync(Stream outputStream, CancellationToken ct, bool isAsync) { byte[] tempBuffer = ArrayPool.Shared.Rent(32768); while (true) { int num = await ReadAsync(tempBuffer, ct, isAsync); if (num <= 0) { break; } if (isAsync) { await outputStream.WriteAsync(tempBuffer, 0, num, ct); } else { outputStream.Write(tempBuffer, 0, num); } } ArrayPool.Shared.Return(tempBuffer); } private async ValueTask SkipToNextEntryAsync(CancellationToken ct, bool isAsync) { long num = entrySize - entryOffset; if (num > 0) { await SkipAsync(num, ct, isAsync); } readBuffer?.Dispose(); readBuffer = null; } } public class TarOutputStream : Stream { private long currBytes; private int assemblyBufferLength; private bool isClosed; protected long currSize; protected byte[] blockBuffer; protected byte[] assemblyBuffer; protected TarBuffer buffer; protected Stream outputStream; protected Encoding nameEncoding; public bool IsStreamOwner { get { return buffer.IsStreamOwner; } set { buffer.IsStreamOwner = value; } } public override bool CanRead => outputStream.CanRead; public override bool CanSeek => outputStream.CanSeek; public override bool CanWrite => outputStream.CanWrite; public override long Length => outputStream.Length; public override long Position { get { return outputStream.Position; } set { outputStream.Position = value; } } public int RecordSize => buffer.RecordSize; private bool IsEntryOpen => currBytes < currSize; [Obsolete("No Encoding for Name field is specified, any non-ASCII bytes will be discarded")] public TarOutputStream(Stream outputStream) : this(outputStream, 20) { } public TarOutputStream(Stream outputStream, Encoding nameEncoding) : this(outputStream, 20, nameEncoding) { } [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("outputStream"); } this.outputStream = outputStream; buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor); assemblyBuffer = ArrayPool.Shared.Rent(512); blockBuffer = ArrayPool.Shared.Rent(512); } public TarOutputStream(Stream outputStream, int blockFactor, Encoding nameEncoding) { if (outputStream == null) { throw new ArgumentNullException("outputStream"); } this.outputStream = outputStream; buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor); assemblyBuffer = ArrayPool.Shared.Rent(512); blockBuffer = ArrayPool.Shared.Rent(512); this.nameEncoding = nameEncoding; } public override long Seek(long offset, SeekOrigin origin) { return outputStream.Seek(offset, origin); } public override void SetLength(long value) { outputStream.SetLength(value); } public override int ReadByte() { return outputStream.ReadByte(); } public override int Read(byte[] buffer, int offset, int count) { return outputStream.Read(buffer, offset, count); } public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return await outputStream.ReadAsync(buffer, offset, count, cancellationToken); } public override void Flush() { outputStream.Flush(); } public override async Task FlushAsync(CancellationToken cancellationToken) { await outputStream.FlushAsync(cancellationToken); } public void Finish() { FinishAsync(CancellationToken.None, isAsync: false).GetAwaiter().GetResult(); } public Task FinishAsync(CancellationToken cancellationToken) { return FinishAsync(cancellationToken, isAsync: true); } private async Task FinishAsync(CancellationToken cancellationToken, bool isAsync) { if (IsEntryOpen) { await CloseEntryAsync(cancellationToken, isAsync); } await WriteEofBlockAsync(cancellationToken, isAsync); } protected override void Dispose(bool disposing) { if (!isClosed) { isClosed = true; Finish(); buffer.Close(); ArrayPool.Shared.Return(assemblyBuffer); ArrayPool.Shared.Return(blockBuffer); } } [Obsolete("Use RecordSize property instead")] public int GetRecordSize() { return buffer.RecordSize; } public Task PutNextEntryAsync(TarEntry entry, CancellationToken cancellationToken) { return PutNextEntryAsync(entry, cancellationToken, isAsync: true); } public void PutNextEntry(TarEntry entry) { PutNextEntryAsync(entry, CancellationToken.None, isAsync: false).GetAwaiter().GetResult(); } private async Task PutNextEntryAsync(TarEntry entry, CancellationToken cancellationToken, bool isAsync) { if (entry == null) { throw new ArgumentNullException("entry"); } int namelen = ((nameEncoding != null) ? nameEncoding.GetByteCount(entry.TarHeader.Name) : entry.TarHeader.Name.Length); if (namelen > 100) { TarHeader obj = new TarHeader { TypeFlag = 76 }; obj.Name += "././@LongLink"; obj.Mode = 420; obj.UserId = entry.UserId; obj.GroupId = entry.GroupId; obj.GroupName = entry.GroupName; obj.UserName = entry.UserName; obj.LinkName = ""; obj.Size = namelen + 1; obj.WriteHeader(blockBuffer, nameEncoding); await buffer.WriteBlockAsync(blockBuffer, 0, cancellationToken, isAsync); int nameCharIndex = 0; while (nameCharIndex < namelen + 1) { Array.Clear(blockBuffer, 0, blockBuffer.Length); TarHeader.GetAsciiBytes(entry.TarHeader.Name, nameCharIndex, blockBuffer, 0, 512, nameEncoding); nameCharIndex += 512; await buffer.WriteBlockAsync(blockBuffer, 0, cancellationToken, isAsync); } } entry.WriteEntryHeader(blockBuffer, nameEncoding); await buffer.WriteBlockAsync(blockBuffer, 0, cancellationToken, isAsync); currBytes = 0L; currSize = (entry.IsDirectory ? 0 : entry.Size); } public Task CloseEntryAsync(CancellationToken cancellationToken) { return CloseEntryAsync(cancellationToken, isAsync: true); } public void CloseEntry() { CloseEntryAsync(CancellationToken.None, isAsync: true).GetAwaiter().GetResult(); } private async Task CloseEntryAsync(CancellationToken cancellationToken, bool isAsync) { if (assemblyBufferLength > 0) { Array.Clear(assemblyBuffer, assemblyBufferLength, assemblyBuffer.Length - assemblyBufferLength); await buffer.WriteBlockAsync(assemblyBuffer, 0, cancellationToken, isAsync); currBytes += assemblyBufferLength; assemblyBufferLength = 0; } if (currBytes < currSize) { throw new TarException($"Entry closed at '{currBytes}' before the '{currSize}' bytes specified in the header were written"); } } public override void WriteByte(byte value) { byte[] array = ArrayPool.Shared.Rent(1); array[0] = value; Write(array, 0, 1); ArrayPool.Shared.Return(array); } public override void Write(byte[] buffer, int offset, int count) { WriteAsync(buffer, offset, count, CancellationToken.None, isAsync: false).GetAwaiter().GetResult(); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return WriteAsync(buffer, offset, count, cancellationToken, isAsync: true); } private async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, bool isAsync) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "Cannot be negative"); } if (buffer.Length - offset < count) { throw new ArgumentException("offset and count combination is invalid"); } if (count < 0) { throw new ArgumentOutOfRangeException("count", "Cannot be negative"); } if (currBytes + count > currSize) { string message = $"request to write '{count}' bytes exceeds size in header of '{currSize}' bytes"; throw new ArgumentOutOfRangeException("count", message); } 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); await this.buffer.WriteBlockAsync(blockBuffer, 0, cancellationToken, isAsync); currBytes += blockBuffer.Length; offset += aLen; count -= aLen; assemblyBufferLength = 0; } else { Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count); offset += count; assemblyBufferLength += count; count -= count; } } while (count > 0) { if (count < blockBuffer.Length) { Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count); assemblyBufferLength += count; break; } await this.buffer.WriteBlockAsync(buffer, offset, cancellationToken, isAsync); int num = blockBuffer.Length; currBytes += num; count -= num; offset += num; } } private async Task WriteEofBlockAsync(CancellationToken cancellationToken, bool isAsync) { Array.Clear(blockBuffer, 0, blockBuffer.Length); await buffer.WriteBlockAsync(blockBuffer, 0, cancellationToken, isAsync); await buffer.WriteBlockAsync(blockBuffer, 0, cancellationToken, isAsync); } } internal static class TarStringExtension { public static string ToTarArchivePath(this string s) { return PathUtils.DropPathRoot(s).Replace(Path.DirectorySeparatorChar, '/'); } } } namespace ICSharpCode.SharpZipLib.Lzw { public sealed class LzwConstants { public const int MAGIC = 8093; public const int MAX_BITS = 16; public const int BIT_MASK = 31; public const int EXTENDED_MASK = 32; public const int RESERVED_MASK = 96; public const int BLOCK_MODE_MASK = 128; public const int HDR_SIZE = 3; public const int INIT_BITS = 9; private LzwConstants() { } } [Serializable] public class LzwException : SharpZipBaseException { public LzwException() { } public LzwException(string message) : base(message) { } public LzwException(string message, Exception innerException) : base(message, innerException) { } protected LzwException(SerializationInfo info, StreamingContext context) : base(info, context) { } } public class LzwInputStream : Stream { private Stream baseInputStream; private bool isClosed; private readonly byte[] one = new byte[1]; private bool headerParsed; private const int TBL_CLEAR = 256; private const int TBL_FIRST = 257; private int[] tabPrefix; private byte[] tabSuffix; private readonly int[] zeros = new int[256]; private byte[] stack; 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; private readonly byte[] data = new byte[8192]; private int bitPos; private int end; private int got; private bool eof; private const int EXTRA = 64; public bool IsStreamOwner { get; set; } = true; public override bool CanRead => baseInputStream.CanRead; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length => got; public override long Position { get { return baseInputStream.Position; } set { throw new NotSupportedException("InflaterInputStream Position not supported"); } } public LzwInputStream(Stream baseInputStream) { this.baseInputStream = baseInputStream; } public override int ReadByte() { if (Read(one, 0, 1) == 1) { return one[0] & 0xFF; } return -1; } public override int Read(byte[] buffer, int offset, int count) { if (!headerParsed) { ParseHeader(); } if (eof) { return 0; } int num = offset; int[] array = tabPrefix; byte[] array2 = tabSuffix; byte[] array3 = stack; int num2 = nBits; int num3 = maxCode; int num4 = maxMaxCode; int num5 = bitMask; int num6 = oldCode; byte b = finChar; int num7 = stackP; int num8 = freeEnt; byte[] array4 = data; int num9 = bitPos; int num10 = array3.Length - num7; if (num10 > 0) { int num11 = ((num10 >= count) ? count : num10); Array.Copy(array3, num7, buffer, offset, num11); offset += num11; count -= num11; num7 += num11; } if (count == 0) { stackP = num7; return offset - num; } while (true) { if (end < 64) { Fill(); } int num12 = ((got > 0) ? (end - end % num2 << 3) : ((end << 3) - (num2 - 1))); while (true) { if (num9 < num12) { if (count == 0) { nBits = num2; maxCode = num3; maxMaxCode = num4; bitMask = num5; oldCode = num6; finChar = b; stackP = num7; freeEnt = num8; bitPos = num9; return offset - num; } if (num8 > num3) { int num13 = num2 << 3; num9 = num9 - 1 + num13 - (num9 - 1 + num13) % num13; num2++; num3 = ((num2 == maxBits) ? num4 : ((1 << num2) - 1)); num5 = (1 << num2) - 1; num9 = ResetBuf(num9); break; } int num14 = num9 >> 3; int num15 = (((array4[num14] & 0xFF) | ((array4[num14 + 1] & 0xFF) << 8) | ((array4[num14 + 2] & 0xFF) << 16)) >> (num9 & 7)) & num5; num9 += num2; if (num6 == -1) { if (num15 >= 256) { throw new LzwException("corrupt input: " + num15 + " > 255"); } b = (byte)(num6 = num15); buffer[offset++] = b; count--; continue; } if (num15 == 256 && blockMode) { Array.Copy(zeros, 0, array, 0, zeros.Length); num8 = 256; int num16 = num2 << 3; num9 = num9 - 1 + num16 - (num9 - 1 + num16) % num16; num2 = 9; num3 = (1 << num2) - 1; num5 = num3; num9 = ResetBuf(num9); break; } int num17 = num15; num7 = array3.Length; if (num15 >= num8) { if (num15 > num8) { throw new LzwException("corrupt input: code=" + num15 + ", freeEnt=" + num8); } array3[--num7] = b; num15 = num6; } while (num15 >= 256) { array3[--num7] = array2[num15]; num15 = array[num15]; } b = array2[num15]; buffer[offset++] = b; count--; num10 = array3.Length - num7; int num18 = ((num10 >= count) ? count : num10); Array.Copy(array3, num7, buffer, offset, num18); offset += num18; count -= num18; num7 += num18; if (num8 < num4) { array[num8] = num6; array2[num8] = b; num8++; } num6 = num17; if (count != 0) { continue; } nBits = num2; maxCode = num3; bitMask = num5; oldCode = num6; finChar = b; stackP = num7; freeEnt = num8; bitPos = num9; return offset - num; } num9 = ResetBuf(num9); if (got > 0) { break; } nBits = num2; maxCode = num3; bitMask = num5; oldCode = num6; finChar = b; stackP = num7; freeEnt = num8; bitPos = num9; eof = true; return offset - num; } } } private int ResetBuf(int bitPosition) { int num = bitPosition >> 3; Array.Copy(data, num, data, 0, end - num); end -= num; 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[] array = new byte[3]; if (baseInputStream.Read(array, 0, array.Length) < 0) { throw new LzwException("Failed to read LZW header"); } if (array[0] != 31 || array[1] != 157) { throw new LzwException($"Wrong LZW header. Magic bytes don't match. 0x{array[0]:x2} 0x{array[1]:x2}"); } blockMode = (array[2] & 0x80) > 0; maxBits = array[2] & 0x1F; if (maxBits > 16) { throw new LzwException("Stream compressed with " + maxBits + " bits, but decompression can only handle " + 16 + " bits."); } if ((array[2] & 0x60) > 0) { throw new LzwException("Unsupported bits set in the header."); } maxMaxCode = 1 << maxBits; nBits = 9; maxCode = (1 << nBits) - 1; bitMask = maxCode; oldCode = -1; finChar = 0; freeEnt = (blockMode ? 257 : 256); tabPrefix = new int[1 << maxBits]; tabSuffix = new byte[1 << maxBits]; stack = new byte[1 << maxBits]; stackP = stack.Length; for (int num = 255; num >= 0; num--) { tabSuffix[num] = (byte)num; } } public override void Flush() { baseInputStream.Flush(); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException("Seek not supported"); } public override void SetLength(long value) { throw new NotSupportedException("InflaterInputStream SetLength not supported"); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException("InflaterInputStream Write not supported"); } public override void WriteByte(byte value) { throw new NotSupportedException("InflaterInputStream WriteByte not supported"); } protected override void Dispose(bool disposing) { if (!isClosed) { isClosed = true; if (IsStreamOwner) { baseInputStream.Dispose(); } } } } } namespace ICSharpCode.SharpZipLib.GZip { public static class GZip { public static void Decompress(Stream inStream, Stream outStream, bool isStreamOwner) { if (inStream == null) { throw new ArgumentNullException("inStream", "Input stream is null"); } if (outStream == null) { throw new ArgumentNullException("outStream", "Output stream is null"); } try { using GZipInputStream gZipInputStream = new GZipInputStream(inStream); gZipInputStream.IsStreamOwner = isStreamOwner; StreamUtils.Copy(gZipInputStream, outStream, new byte[4096]); } finally { if (isStreamOwner) { outStream.Dispose(); } } } public static void Compress(Stream inStream, Stream outStream, bool isStreamOwner, int bufferSize = 512, int level = 6) { if (inStream == null) { throw new ArgumentNullException("inStream", "Input stream is null"); } if (outStream == null) { throw new ArgumentNullException("outStream", "Output stream is null"); } if (bufferSize < 512) { throw new ArgumentOutOfRangeException("bufferSize", "Deflate buffer size must be >= 512"); } if (level < 0 || level > 9) { throw new ArgumentOutOfRangeException("level", "Compression level must be 0-9"); } try { using GZipOutputStream gZipOutputStream = new GZipOutputStream(outStream, bufferSize); gZipOutputStream.SetLevel(level); gZipOutputStream.IsStreamOwner = isStreamOwner; StreamUtils.Copy(inStream, gZipOutputStream, new byte[bufferSize]); } finally { if (isStreamOwner) { inStream.Dispose(); } } } } public sealed class GZipConstants { public const byte ID1 = 31; public const byte ID2 = 139; public const byte CompressionMethodDeflate = 8; public static Encoding Encoding { get { try { return Encoding.GetEncoding(1252); } catch { return Encoding.ASCII; } } } } [Flags] public enum GZipFlags : byte { FTEXT = 1, FHCRC = 2, FEXTRA = 4, FNAME = 8, FCOMMENT = 0x10 } [Serializable] public class GZipException : SharpZipBaseException { public GZipException() { } public GZipException(string message) : base(message) { } public GZipException(string message, Exception innerException) : base(message, innerException) { } protected GZipException(SerializationInfo info, StreamingContext context) : base(info, context) { } } public class GZipInputStream : InflaterInputStream { protected Crc32 crc; private bool readGZIPHeader; private bool completedLastBlock; private string fileName; public GZipInputStream(Stream baseInputStream) : this(baseInputStream, 4096) { } public GZipInputStream(Stream baseInputStream, int size) : base(baseInputStream, new Inflater(noHeader: true), size) { } public override int Read(byte[] buffer, int offset, int count) { int num; do { if (!readGZIPHeader) { try { if (!ReadHeader()) { return 0; } } catch (Exception ex) when (completedLastBlock && (ex is GZipException || ex is EndOfStreamException)) { return 0; } } num = base.Read(buffer, offset, count); if (num > 0) { crc.Update(new ArraySegment(buffer, offset, num)); } if (inf.IsFinished) { ReadFooter(); } } while (num <= 0 && count != 0); return num; } public string GetFilename() { return fileName; } private bool ReadHeader() { this.crc = new Crc32(); if (inputBuffer.Available <= 0) { inputBuffer.Fill(); if (inputBuffer.Available <= 0) { return false; } } Crc32 crc = new Crc32(); byte b = inputBuffer.ReadLeByte(); crc.Update(b); if (b != 31) { throw new GZipException("Error GZIP header, first magic byte doesn't match"); } b = inputBuffer.ReadLeByte(); if (b != 139) { throw new GZipException("Error GZIP header, second magic byte doesn't match"); } crc.Update(b); byte b2 = inputBuffer.ReadLeByte(); if (b2 != 8) { throw new GZipException("Error GZIP header, data not in deflate format"); } crc.Update(b2); byte b3 = inputBuffer.ReadLeByte(); crc.Update(b3); if ((b3 & 0xE0) != 0) { throw new GZipException("Reserved flag bits in GZIP header != 0"); } GZipFlags gZipFlags = (GZipFlags)b3; for (int i = 0; i < 6; i++) { crc.Update(inputBuffer.ReadLeByte()); } if (gZipFlags.HasFlag(GZipFlags.FEXTRA)) { byte b4 = inputBuffer.ReadLeByte(); byte b5 = inputBuffer.ReadLeByte(); crc.Update(b4); crc.Update(b5); int num = (b5 << 8) | b4; for (int j = 0; j < num; j++) { crc.Update(inputBuffer.ReadLeByte()); } } if (gZipFlags.HasFlag(GZipFlags.FNAME)) { byte[] array = new byte[1024]; int num2 = 0; int num3; while ((num3 = inputBuffer.ReadLeByte()) > 0) { if (num2 < 1024) { array[num2++] = (byte)num3; } crc.Update(num3); } crc.Update(num3); fileName = GZipConstants.Encoding.GetString(array, 0, num2); } else { fileName = null; } if (gZipFlags.HasFlag(GZipFlags.FCOMMENT)) { int bval; while ((bval = inputBuffer.ReadLeByte()) > 0) { crc.Update(bval); } crc.Update(bval); } if (gZipFlags.HasFlag(GZipFlags.FHCRC)) { byte num4 = inputBuffer.ReadLeByte(); if (num4 < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } int num5 = inputBuffer.ReadLeByte(); if (num5 < 0) { throw new EndOfStreamException("EOS reading GZIP header"); } if (((num4 << 8) | num5) != ((int)crc.Value & 0xFFFF)) { throw new GZipException("Header CRC value mismatch"); } } readGZIPHeader = true; return true; } private void ReadFooter() { byte[] array = new byte[8]; long num = inf.TotalOut & 0xFFFFFFFFu; inputBuffer.Available += inf.RemainingInput; inf.Reset(); int num2 = 8; while (num2 > 0) { int num3 = inputBuffer.ReadClearTextBuffer(array, 8 - num2, num2); if (num3 <= 0) { throw new EndOfStreamException("EOS reading GZIP footer"); } num2 -= num3; } int num4 = (array[0] & 0xFF) | ((array[1] & 0xFF) << 8) | ((array[2] & 0xFF) << 16) | (array[3] << 24); if (num4 != (int)crc.Value) { throw new GZipException("GZIP crc sum mismatch, theirs \"" + num4 + "\" and ours \"" + (int)crc.Value); } uint num5 = (uint)((array[4] & 0xFF) | ((array[5] & 0xFF) << 8) | ((array[6] & 0xFF) << 16) | (array[7] << 24)); if (num != num5) { throw new GZipException("Number of bytes mismatch in footer"); } readGZIPHeader = false; completedLastBlock = true; } } public class GZipOutputStream : DeflaterOutputStream { private enum OutputState { Header, Footer, Finished, Closed } protected Crc32 crc = new Crc32(); private OutputState state_; private string fileName; private GZipFlags flags; public string FileName { get { return fileName; } set { fileName = CleanFilename(value); if (string.IsNullOrEmpty(fileName)) { flags &= ~GZipFlags.FNAME; } else { flags |= GZipFlags.FNAME; } } } public GZipOutputStream(Stream baseOutputStream) : this(baseOutputStream, 4096) { } public GZipOutputStream(Stream baseOutputStream, int size) : base(baseOutputStream, new Deflater(-1, noZlibHeaderOrFooter: true), size) { } public void SetLevel(int level) { if (level < 0 || level > 9) { throw new ArgumentOutOfRangeException("level", "Compression level must be 0-9"); } deflater_.SetLevel(level); } public int GetLevel() { return deflater_.GetLevel(); } 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); } protected override void Dispose(bool disposing) { try { Finish(); } finally { if (state_ != OutputState.Closed) { state_ = OutputState.Closed; if (base.IsStreamOwner) { baseOutputStream_.Dispose(); } } } } public override void Flush() { if (state_ == OutputState.Header) { WriteHeader(); } base.Flush(); } public override void Finish() { if (state_ == OutputState.Header) { WriteHeader(); } if (state_ == OutputState.Footer) { state_ = OutputState.Finished; base.Finish(); byte[] footer = GetFooter(); baseOutputStream_.Write(footer, 0, footer.Length); } } public override async Task FlushAsync(CancellationToken ct) { await WriteHeaderAsync(); await base.FlushAsync(ct); } public override async Task FinishAsync(CancellationToken ct) { if (state_ == OutputState.Header) { await WriteHeaderAsync(); } if (state_ == OutputState.Footer) { state_ = OutputState.Finished; await base.FinishAsync(ct); byte[] footer = GetFooter(); await baseOutputStream_.WriteAsync(footer, 0, footer.Length, ct); } } private byte[] GetFooter() { uint num = (uint)(deflater_.TotalIn & 0xFFFFFFFFu); uint num2 = (uint)(crc.Value & 0xFFFFFFFFu); return new byte[8] { (byte)num2, (byte)(num2 >> 8), (byte)(num2 >> 16), (byte)(num2 >> 24), (byte)num, (byte)(num >> 8), (byte)(num >> 16), (byte)(num >> 24) }; } private byte[] GetHeader() { int num = (int)((DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000); byte[] obj = new byte[10] { 31, 139, 8, 0, 0, 0, 0, 0, 0, 255 }; obj[3] = (byte)flags; obj[4] = (byte)num; obj[5] = (byte)(num >> 8); obj[6] = (byte)(num >> 16); obj[7] = (byte)(num >> 24); byte[] array = obj; if (!flags.HasFlag(GZipFlags.FNAME)) { return array; } return array.Concat(GZipConstants.Encoding.GetBytes(fileName)).Concat(new byte[1]).ToArray(); } private static string CleanFilename(string path) { return path.Substring(path.LastIndexOf('/') + 1); } private void WriteHeader() { if (state_ == OutputState.Header) { state_ = OutputState.Footer; byte[] header = GetHeader(); baseOutputStream_.Write(header, 0, header.Length); } } private async Task WriteHeaderAsync() { if (state_ == OutputState.Header) { state_ = OutputState.Footer; byte[] header = GetHeader(); await baseOutputStream_.WriteAsync(header, 0, header.Length); } } } } namespace ICSharpCode.SharpZipLib.Encryption { public abstract class PkzipClassic : SymmetricAlgorithm { public static byte[] GenerateKeys(byte[] seed) { if (seed == null) { throw new ArgumentNullException("seed"); } if (seed.Length == 0) { throw new ArgumentException("Length is zero", "seed"); } uint[] array = new uint[3] { 305419896u, 591751049u, 878082192u }; for (int i = 0; i < seed.Length; i++) { array[0] = Crc32.ComputeCrc32(array[0], seed[i]); array[1] = array[1] + (byte)array[0]; array[1] = array[1] * 134775813 + 1; array[2] = Crc32.ComputeCrc32(array[2], (byte)(array[1] >> 24)); } return new byte[12] { (byte)(array[0] & 0xFF), (byte)((array[0] >> 8) & 0xFF), (byte)((array[0] >> 16) & 0xFF), (byte)((array[0] >> 24) & 0xFF), (byte)(array[1] & 0xFF), (byte)((array[1] >> 8) & 0xFF), (byte)((array[1] >> 16) & 0xFF), (byte)((array[1] >> 24) & 0xFF), (byte)(array[2] & 0xFF), (byte)((array[2] >> 8) & 0xFF), (byte)((array[2] >> 16) & 0xFF), (byte)((array[2] >> 24) & 0xFF) }; } } internal class PkzipClassicCryptoBase { private uint[] keys; protected byte TransformByte() { uint num = (keys[2] & 0xFFFF) | 2; return (byte)(num * (num ^ 1) >> 8); } protected void SetKeys(byte[] keyData) { if (keyData == null) { throw new ArgumentNullException("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]); } 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)); } protected void Reset() { keys[0] = 0u; keys[1] = 0u; keys[2] = 0u; } } internal class PkzipClassicEncryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform, IDisposable { public bool CanReuseTransform => true; public int InputBlockSize => 1; public int OutputBlockSize => 1; public bool CanTransformMultipleBlocks => true; internal PkzipClassicEncryptCryptoTransform(byte[] keyBlock) { SetKeys(keyBlock); } public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { byte[] array = new byte[inputCount]; TransformBlock(inputBuffer, inputOffset, inputCount, array, 0); return array; } public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { for (int i = inputOffset; i < inputOffset + inputCount; i++) { byte ch = inputBuffer[i]; outputBuffer[outputOffset++] = (byte)(inputBuffer[i] ^ TransformByte()); UpdateKeys(ch); } return inputCount; } public void Dispose() { Reset(); } } internal class PkzipClassicDecryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform, IDisposable { public bool CanReuseTransform => true; public int InputBlockSize => 1; public int OutputBlockSize => 1; public bool CanTransformMultipleBlocks => true; internal PkzipClassicDecryptCryptoTransform(byte[] keyBlock) { SetKeys(keyBlock); } public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { byte[] array = new byte[inputCount]; TransformBlock(inputBuffer, inputOffset, inputCount, array, 0); return array; } public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { for (int i = inputOffset; i < inputOffset + inputCount; i++) { byte b = (byte)(inputBuffer[i] ^ TransformByte()); outputBuffer[outputOffset++] = b; UpdateKeys(b); } return inputCount; } public void Dispose() { Reset(); } } public sealed class PkzipClassicManaged : PkzipClassic { private byte[] key_; public override int BlockSize { get { return 8; } set { if (value != 8) { throw new CryptographicException("Block size is invalid"); } } } public override KeySizes[] LegalKeySizes => new KeySizes[1] { new KeySizes(96, 96, 0) }; public override KeySizes[] LegalBlockSizes => new KeySizes[1] { new KeySizes(8, 8, 0) }; public override byte[] Key { get { if (key_ == null) { GenerateKey(); } return (byte[])key_.Clone(); } set { if (value == null) { throw new ArgumentNullException("value"); } if (value.Length != 12) { throw new CryptographicException("Key size is illegal"); } key_ = (byte[])value.Clone(); } } public override void GenerateIV() { } public override void GenerateKey() { key_ = new byte[12]; using RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create(); randomNumberGenerator.GetBytes(key_); } public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) { key_ = rgbKey; return new PkzipClassicEncryptCryptoTransform(Key); } public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) { key_ = rgbKey; return new PkzipClassicDecryptCryptoTransform(Key); } } internal class ZipAESStream : CryptoStream { public const int AUTH_CODE_LENGTH = 10; private const int CRYPTO_BLOCK_SIZE = 16; private const int BLOCK_AND_AUTH = 26; private Stream _stream; private ZipAESTransform _transform; private byte[] _slideBuffer; private int _slideBufStartPos; private int _slideBufFreePos; private byte[] _transformBuffer; private int _transformBufferFreePos; private int _transformBufferStartPos; private bool HasBufferedData { get { if (_transformBuffer != null) { return _transformBufferStartPos < _transformBufferFreePos; } return false; } } public ZipAESStream(Stream stream, ZipAESTransform transform, CryptoStreamMode mode) : base(stream, transform, mode) { _stream = stream; _transform = transform; _slideBuffer = new byte[1024]; if (mode != CryptoStreamMode.Read) { throw new Exception("ZipAESStream only for read"); } } public override int Read(byte[] buffer, int offset, int count) { if (count == 0) { return 0; } int num = 0; if (HasBufferedData) { num = ReadBufferedData(buffer, offset, count); if (num == count) { return num; } offset += num; count -= num; } if (_slideBuffer != null) { num += ReadAndTransform(buffer, offset, count); } return num; } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return Task.FromResult(Read(buffer, offset, count)); } private int ReadAndTransform(byte[] buffer, int offset, int count) { int num = 0; while (num < count) { int count2 = count - num; int num2 = _slideBufFreePos - _slideBufStartPos; int num3 = 26 - num2; if (_slideBuffer.Length - _slideBufFreePos < num3) { int num4 = 0; int num5 = _slideBufStartPos; while (num5 < _slideBufFreePos) { _slideBuffer[num4] = _slideBuffer[num5]; num5++; num4++; } _slideBufFreePos -= _slideBufStartPos; _slideBufStartPos = 0; } int num6 = StreamUtils.ReadRequestedBytes(_stream, _slideBuffer, _slideBufFreePos, num3); _slideBufFreePos += num6; num2 = _slideBufFreePos - _slideBufStartPos; if (num2 >= 26) { int num7 = TransformAndBufferBlock(buffer, offset, count2, 16); num += num7; offset += num7; continue; } if (num2 > 10) { int blockSize = num2 - 10; num += TransformAndBufferBlock(buffer, offset, count2, blockSize); } else if (num2 < 10) { throw new ZipException("Internal error missed auth code"); } byte[] authCode = _transform.GetAuthCode(); for (int i = 0; i < 10; i++) { if (authCode[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\nThe file may be damaged."); } } _slideBuffer = null; break; } return num; } private int ReadBufferedData(byte[] buffer, int offset, int count) { int num = Math.Min(count, _transformBufferFreePos - _transformBufferStartPos); Array.Copy(_transformBuffer, _transformBufferStartPos, buffer, offset, num); _transformBufferStartPos += num; return num; } private int TransformAndBufferBlock(byte[] buffer, int offset, int count, int blockSize) { bool num = blockSize > count; if (num && _transformBuffer == null) { _transformBuffer = new byte[16]; } byte[] outputBuffer = (num ? _transformBuffer : buffer); int outputOffset = ((!num) ? offset : 0); _transform.TransformBlock(_slideBuffer, _slideBufStartPos, blockSize, outputBuffer, outputOffset); _slideBufStartPos += blockSize; if (!num) { return blockSize; } Array.Copy(_transformBuffer, 0, buffer, offset, count); _transformBufferStartPos = count; _transformBufferFreePos = blockSize; return count; } public override void Write(byte[] buffer, int offset, int count) { throw new NotImplementedException(); } } internal class ZipAESTransform : ICryptoTransform, IDisposable { private const int PWD_VER_LENGTH = 2; private const int KEY_ROUNDS = 1000; 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; private bool _writeMode; public byte[] PwdVerifier => _pwdVerifier; public int InputBlockSize => _blockSize; public int OutputBlockSize => _blockSize; public bool CanTransformMultipleBlocks => true; public bool CanReuseTransform => true; public ZipAESTransform(string key, byte[] saltBytes, int blockSize, bool writeMode) { if (blockSize != 16 && blockSize != 32) { 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); } _blockSize = blockSize; _encryptBuffer = new byte[_blockSize]; _encrPos = 16; Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(key, saltBytes, 1000); Aes aes = Aes.Create(); aes.Mode = CipherMode.ECB; _counterNonce = new byte[_blockSize]; byte[] bytes = rfc2898DeriveBytes.GetBytes(_blockSize); byte[] bytes2 = rfc2898DeriveBytes.GetBytes(_blockSize); _encryptor = aes.CreateEncryptor(bytes, new byte[16]); _pwdVerifier = rfc2898DeriveBytes.GetBytes(2); _hmacsha1 = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA1, bytes2); _writeMode = writeMode; } public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (!_writeMode) { _hmacsha1.AppendData(inputBuffer, inputOffset, inputCount); } for (int i = 0; i < inputCount; i++) { if (_encrPos == 16) { int num = 0; while (++_counterNonce[num] == 0) { num++; } _encryptor.TransformBlock(_counterNonce, 0, _blockSize, _encryptBuffer, 0); _encrPos = 0; } outputBuffer[i + outputOffset] = (byte)(inputBuffer[i + inputOffset] ^ _encryptBuffer[_encrPos++]); } if (_writeMode) { _hmacsha1.AppendData(outputBuffer, outputOffset, inputCount); } return inputCount; } public byte[] GetAuthCode() { return _authCode ?? (_authCode = _hmacsha1.GetHashAndReset()); } public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { byte[] array = Array.Empty(); if (inputCount != 0) { if (inputCount > 10) { int num = inputCount - 10; array = new byte[num]; TransformBlock(inputBuffer, inputOffset, num, array, 0); } else if (inputCount < 10) { throw new ZipException("Auth code missing from input stream"); } _authCode = _hmacsha1.GetHashAndReset(); } return array; } public void Dispose() { _encryptor.Dispose(); } } } namespace ICSharpCode.SharpZipLib.Core { internal static class ByteOrderStreamExtensions { internal static byte[] SwappedBytes(ushort value) { return new byte[2] { (byte)value, (byte)(value >> 8) }; } internal static byte[] SwappedBytes(short value) { return new byte[2] { (byte)value, (byte)(value >> 8) }; } internal static byte[] SwappedBytes(uint value) { return new byte[4] { (byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24) }; } internal static byte[] SwappedBytes(int value) { return new byte[4] { (byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24) }; } internal static byte[] SwappedBytes(long value) { return new byte[8] { (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) { return new byte[8] { (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) { return (long)(bytes[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 ulong SwappedU64(byte[] bytes) { return bytes[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) { return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static uint SwappedU32(byte[] bytes) { return (uint)SwappedS32(bytes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static short SwappedS16(byte[] bytes) { return (short)(bytes[0] | (bytes[1] << 8)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ushort SwappedU16(byte[] bytes) { return (ushort)SwappedS16(bytes); } internal static byte[] ReadBytes(this Stream stream, int count) { byte[] array = new byte[count]; int num = count; while (num > 0) { int num2 = stream.Read(array, count - num, num); if (num2 < 1) { throw new EndOfStreamException(); } num -= num2; } return array; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ReadLEShort(this Stream stream) { return SwappedS16(stream.ReadBytes(2)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ReadLEInt(this Stream stream) { return SwappedS32(stream.ReadBytes(4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long ReadLELong(this Stream stream) { return SwappedS64(stream.ReadBytes(8)); } [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, CancellationToken ct) { await stream.WriteAsync(SwappedBytes(value), 0, 2, ct); } [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, CancellationToken ct) { await stream.WriteAsync(SwappedBytes(value), 0, 2, ct); } [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, CancellationToken ct) { await stream.WriteAsync(SwappedBytes(value), 0, 4, ct); } [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, CancellationToken ct) { await stream.WriteAsync(SwappedBytes(value), 0, 4, ct); } [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, CancellationToken ct) { await stream.WriteAsync(SwappedBytes(value), 0, 8, ct); } [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, CancellationToken ct) { await stream.WriteAsync(SwappedBytes(value), 0, 8, ct); } } internal static class Empty { public static T[] Array() { return System.Array.Empty(); } } internal sealed class ExactMemoryPool : MemoryPool { private sealed class ExactMemoryPoolBuffer : IMemoryOwner, IDisposable { private T[] array; private readonly int size; public Memory Memory => new Memory(array ?? throw new ObjectDisposedException("ExactMemoryPoolBuffer")).Slice(0, size); public ExactMemoryPoolBuffer(int size) { this.size = size; array = ArrayPool.Shared.Rent(size); } public void Dispose() { T[] array = this.array; if (array != null) { this.array = null; ArrayPool.Shared.Return(array); } } } public new static readonly MemoryPool Shared = new ExactMemoryPool(); public override int MaxBufferSize => int.MaxValue; public override IMemoryOwner Rent(int bufferSize = -1) { if ((uint)bufferSize > 2147483647u || bufferSize < 0) { throw new ArgumentOutOfRangeException("bufferSize"); } return new ExactMemoryPoolBuffer(bufferSize); } protected override void Dispose(bool disposing) { } } public class ScanEventArgs : EventArgs { private string name_; private bool continueRunning_ = true; public string Name => name_; public bool ContinueRunning { get { return continueRunning_; } set { continueRunning_ = value; } } public ScanEventArgs(string name) { name_ = name; } } public class ProgressEventArgs : EventArgs { private string name_; private long processed_; private long target_; private bool continueRunning_ = true; public string Name => name_; public bool ContinueRunning { get { return continueRunning_; } set { continueRunning_ = value; } } public float PercentComplete { get { if (target_ <= 0) { return 0f; } return (float)processed_ / (float)target_ * 100f; } } public long Processed => processed_; public long Target => target_; public ProgressEventArgs(string name, long processed, long target) { name_ = name; processed_ = processed; target_ = target; } } public class DirectoryEventArgs : ScanEventArgs { private readonly bool hasMatchingFiles_; public bool HasMatchingFiles => hasMatchingFiles_; public DirectoryEventArgs(string name, bool hasMatchingFiles) : base(name) { hasMatchingFiles_ = hasMatchingFiles; } } public class ScanFailureEventArgs : EventArgs { private string name_; private Exception exception_; private bool continueRunning_; public string Name => name_; public Exception Exception => exception_; public bool ContinueRunning { get { return continueRunning_; } set { continueRunning_ = value; } } public ScanFailureEventArgs(string name, Exception e) { name_ = name; exception_ = e; continueRunning_ = true; } } public delegate void ProcessFileHandler(object sender, ScanEventArgs e); public delegate void ProgressHandler(object sender, ProgressEventArgs e); public delegate void CompletedFileHandler(object sender, ScanEventArgs e); public delegate void DirectoryFailureHandler(object sender, ScanFailureEventArgs e); public delegate void FileFailureHandler(object sender, ScanFailureEventArgs e); public class FileSystemScanner { public ProcessFileHandler ProcessFile; public CompletedFileHandler CompletedFile; public DirectoryFailureHandler DirectoryFailure; public FileFailureHandler FileFailure; private IScanFilter fileFilter_; private IScanFilter directoryFilter_; private bool alive_; public event EventHandler ProcessDirectory; public FileSystemScanner(string filter) { fileFilter_ = new PathFilter(filter); } public FileSystemScanner(string fileFilter, string directoryFilter) { fileFilter_ = new PathFilter(fileFilter); directoryFilter_ = new PathFilter(directoryFilter); } public FileSystemScanner(IScanFilter fileFilter) { fileFilter_ = fileFilter; } public FileSystemScanner(IScanFilter fileFilter, IScanFilter directoryFilter) { fileFilter_ = fileFilter; directoryFilter_ = directoryFilter; } private bool OnDirectoryFailure(string directory, Exception e) { DirectoryFailureHandler directoryFailure = DirectoryFailure; bool num = directoryFailure != null; if (num) { ScanFailureEventArgs e2 = new ScanFailureEventArgs(directory, e); directoryFailure(this, e2); alive_ = e2.ContinueRunning; } return num; } private bool OnFileFailure(string file, Exception e) { bool num = FileFailure != null; if (num) { ScanFailureEventArgs e2 = new ScanFailureEventArgs(file, e); FileFailure(this, e2); alive_ = e2.ContinueRunning; } return num; } private void OnProcessFile(string file) { ProcessFileHandler processFile = ProcessFile; if (processFile != null) { ScanEventArgs e = new ScanEventArgs(file); processFile(this, e); alive_ = e.ContinueRunning; } } private void OnCompleteFile(string file) { CompletedFileHandler completedFile = CompletedFile; if (completedFile != null) { ScanEventArgs e = new ScanEventArgs(file); completedFile(this, e); alive_ = e.ContinueRunning; } } private void OnProcessDirectory(string directory, bool hasMatchingFiles) { EventHandler eventHandler = this.ProcessDirectory; if (eventHandler != null) { DirectoryEventArgs e = new DirectoryEventArgs(directory, hasMatchingFiles); eventHandler(this, e); alive_ = e.ContinueRunning; } } public void Scan(string directory, bool recurse) { alive_ = true; ScanDir(directory, recurse); } private void ScanDir(string directory, bool recurse) { try { string[] files = Directory.GetFiles(directory); bool flag = false; for (int i = 0; i < files.Length; i++) { if (!fileFilter_.IsMatch(files[i])) { files[i] = null; } else { flag = true; } } OnProcessDirectory(directory, flag); if (alive_ && flag) { string[] array = files; foreach (string text in array) { try { if (text != null) { OnProcessFile(text); if (!alive_) { break; } } } catch (Exception e) { if (!OnFileFailure(text, e)) { throw; } } } } } catch (Exception e2) { if (!OnDirectoryFailure(directory, e2)) { throw; } } if (!(alive_ && recurse)) { return; } try { string[] array = Directory.GetDirectories(directory); foreach (string text2 in array) { if (directoryFilter_ == null || directoryFilter_.IsMatch(text2)) { ScanDir(text2, recurse: true); if (!alive_) { break; } } } } catch (Exception e3) { if (!OnDirectoryFailure(directory, e3)) { throw; } } } } public interface INameTransform { string TransformFile(string name); string TransformDirectory(string name); } [Serializable] public class InvalidNameException : SharpZipBaseException { public InvalidNameException() : base("An invalid name was specified") { } public InvalidNameException(string message) : base(message) { } public InvalidNameException(string message, Exception innerException) : base(message, innerException) { } protected InvalidNameException(SerializationInfo info, StreamingContext context) : base(info, context) { } } public interface IScanFilter { bool IsMatch(string name); } public class NameFilter : IScanFilter { private string filter_; private List inclusions_; private List exclusions_; public NameFilter(string filter) { filter_ = filter; inclusions_ = new List(); exclusions_ = new List(); Compile(); } public static bool IsValidExpression(string expression) { bool result = true; try { new Regex(expression, RegexOptions.IgnoreCase | RegexOptions.Singleline); } catch (ArgumentException) { result = false; } return result; } public static bool IsValidFilterExpression(string toTest) { bool result = true; try { if (toTest != null) { string[] array = SplitQuoted(toTest); for (int i = 0; i < array.Length; i++) { if (array[i] != null && array[i].Length > 0) { string pattern = ((array[i][0] == '+') ? array[i].Substring(1, array[i].Length - 1) : ((array[i][0] != '-') ? array[i] : array[i].Substring(1, array[i].Length - 1))); new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); } } } } catch (ArgumentException) { result = false; } return result; } public static string[] SplitQuoted(string original) { char c = '\\'; char[] array = new char[1] { ';' }; List list = new List(); if (!string.IsNullOrEmpty(original)) { int num = -1; StringBuilder stringBuilder = new StringBuilder(); while (num < original.Length) { num++; if (num >= original.Length) { list.Add(stringBuilder.ToString()); } else if (original[num] == c) { num++; if (num >= original.Length) { throw new ArgumentException("Missing terminating escape character", "original"); } if (Array.IndexOf(array, original[num]) < 0) { stringBuilder.Append(c); } stringBuilder.Append(original[num]); } else if (Array.IndexOf(array, original[num]) >= 0) { list.Add(stringBuilder.ToString()); stringBuilder.Length = 0; } else { stringBuilder.Append(original[num]); } } } return list.ToArray(); } public override string ToString() { return filter_; } public bool IsIncluded(string name) { bool result = false; if (inclusions_.Count == 0) { result = true; } else { foreach (Regex item in inclusions_) { if (item.IsMatch(name)) { result = true; break; } } } return result; } public bool IsExcluded(string name) { bool result = false; foreach (Regex item in exclusions_) { if (item.IsMatch(name)) { result = true; break; } } return result; } public bool IsMatch(string name) { if (IsIncluded(name)) { return !IsExcluded(name); } return false; } private void Compile() { if (filter_ == null) { return; } string[] array = SplitQuoted(filter_); for (int i = 0; i < array.Length; i++) { if (array[i] != null && array[i].Length > 0) { bool num = array[i][0] != '-'; string pattern = ((array[i][0] == '+') ? array[i].Substring(1, array[i].Length - 1) : ((array[i][0] != '-') ? array[i] : array[i].Substring(1, array[i].Length - 1))); if (num) { inclusions_.Add(new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)); } else { exclusions_.Add(new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)); } } } } } public class PathFilter : IScanFilter { private readonly NameFilter nameFilter_; public PathFilter(string filter) { nameFilter_ = new NameFilter(filter); } public virtual bool IsMatch(string name) { bool result = false; if (name != null) { string name2 = ((name.Length > 0) ? Path.GetFullPath(name) : ""); result = nameFilter_.IsMatch(name2); } return result; } } public class ExtendedPathFilter : PathFilter { private long minSize_; private long maxSize_ = long.MaxValue; private DateTime minDate_ = DateTime.MinValue; private DateTime maxDate_ = DateTime.MaxValue; public long MinSize { get { return minSize_; } set { if (value < 0 || maxSize_ < value) { throw new ArgumentOutOfRangeException("value"); } minSize_ = value; } } public long MaxSize { get { return maxSize_; } set { if (value < 0 || minSize_ > value) { throw new ArgumentOutOfRangeException("value"); } maxSize_ = value; } } public DateTime MinDate { get { return minDate_; } set { if (value > maxDate_) { throw new ArgumentOutOfRangeException("value", "Exceeds MaxDate"); } minDate_ = value; } } public DateTime MaxDate { get { return maxDate_; } set { if (minDate_ > value) { throw new ArgumentOutOfRangeException("value", "Exceeds MinDate"); } maxDate_ = value; } } public ExtendedPathFilter(string filter, long minSize, long maxSize) : base(filter) { MinSize = minSize; MaxSize = maxSize; } public ExtendedPathFilter(string filter, DateTime minDate, DateTime maxDate) : base(filter) { MinDate = minDate; MaxDate = maxDate; } public ExtendedPathFilter(string filter, long minSize, long maxSize, DateTime minDate, DateTime maxDate) : base(filter) { MinSize = minSize; MaxSize = maxSize; MinDate = minDate; MaxDate = maxDate; } public override bool IsMatch(string name) { bool flag = base.IsMatch(name); if (flag) { FileInfo fileInfo = new FileInfo(name); flag = MinSize <= fileInfo.Length && MaxSize >= fileInfo.Length && MinDate <= fileInfo.LastWriteTime && MaxDate >= fileInfo.LastWriteTime; } return flag; } } [Obsolete("Use ExtendedPathFilter instead")] public class NameAndSizeFilter : PathFilter { private long minSize_; private long maxSize_ = long.MaxValue; public long MinSize { get { return minSize_; } set { if (value < 0 || maxSize_ < value) { throw new ArgumentOutOfRangeException("value"); } minSize_ = value; } } public long MaxSize { get { return maxSize_; } set { if (value < 0 || minSize_ > value) { throw new ArgumentOutOfRangeException("value"); } maxSize_ = value; } } public NameAndSizeFilter(string filter, long minSize, long maxSize) : base(filter) { MinSize = minSize; MaxSize = maxSize; } public override bool IsMatch(string name) { bool flag = base.IsMatch(name); if (flag) { long length = new FileInfo(name).Length; flag = MinSize <= length && MaxSize >= length; } return flag; } } public static class PathUtils { public static string DropPathRoot(string path) { char[] invalidChars = Path.GetInvalidPathChars(); bool cleanRootSep = path.Length >= 3 && path[1] == ':' && path[2] == ':'; int num; for (num = Path.GetPathRoot(new string(path.Take(258).Select((char c, int i) => (!invalidChars.Contains(c) && !(i == 2 && cleanRootSep)) ? c : '_').ToArray())).Length; path.Length > num && (path[num] == '/' || path[num] == '\\'); num++) { } return path.Substring(num); } public static string GetTempFileName(string original = null) { string tempPath = Path.GetTempPath(); string text; do { text = ((original == null) ? Path.Combine(tempPath, Path.GetRandomFileName()) : (original + "." + Path.GetRandomFileName())); } while (File.Exists(text)); return text; } } public static class StreamUtils { public static void ReadFully(Stream stream, byte[] buffer) { ReadFully(stream, buffer, 0, buffer.Length); } public static void ReadFully(Stream stream, byte[] buffer, int offset, int count) { if (stream == null) { throw new ArgumentNullException("stream"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0 || offset > buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0 || offset + count > buffer.Length) { throw new ArgumentOutOfRangeException("count"); } while (count > 0) { int num = stream.Read(buffer, offset, count); if (num <= 0) { throw new EndOfStreamException(); } offset += num; count -= num; } } public static int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, int count) { if (stream == null) { throw new ArgumentNullException("stream"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0 || offset > buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0 || offset + count > buffer.Length) { throw new ArgumentOutOfRangeException("count"); } int num = 0; while (count > 0) { int num2 = stream.Read(buffer, offset, count); if (num2 <= 0) { break; } offset += num2; count -= num2; num += num2; } return num; } public static void Copy(Stream source, Stream destination, byte[] buffer) { if (source == null) { throw new ArgumentNullException("source"); } if (destination == null) { throw new ArgumentNullException("destination"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if (buffer.Length < 128) { throw new ArgumentException("Buffer is too small", "buffer"); } bool flag = true; while (flag) { int num = source.Read(buffer, 0, buffer.Length); if (num > 0) { destination.Write(buffer, 0, num); continue; } destination.Flush(); flag = false; } } 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, -1L); } 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("source"); } if (destination == null) { throw new ArgumentNullException("destination"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if (buffer.Length < 128) { throw new ArgumentException("Buffer is too small", "buffer"); } if (progressHandler == null) { throw new ArgumentNullException("progressHandler"); } bool flag = true; DateTime now = DateTime.Now; long num = 0L; long target = 0L; if (fixedTarget >= 0) { target = fixedTarget; } else if (source.CanSeek) { target = source.Length - source.Position; } ProgressEventArgs e = new ProgressEventArgs(name, num, target); progressHandler(sender, e); bool flag2 = true; while (flag) { int num2 = source.Read(buffer, 0, buffer.Length); if (num2 > 0) { num += num2; flag2 = false; destination.Write(buffer, 0, num2); } else { destination.Flush(); flag = false; } if (DateTime.Now - now > updateInterval) { flag2 = true; now = DateTime.Now; e = new ProgressEventArgs(name, num, target); progressHandler(sender, e); flag = e.ContinueRunning; } } if (!flag2) { e = new ProgressEventArgs(name, num, target); progressHandler(sender, e); } } internal static async Task WriteProcToStreamAsync(this Stream targetStream, MemoryStream bufferStream, Action writeProc, CancellationToken ct) { bufferStream.SetLength(0L); writeProc(bufferStream); bufferStream.Position = 0L; await bufferStream.CopyToAsync(targetStream, 81920, ct); bufferStream.SetLength(0L); } internal static async Task WriteProcToStreamAsync(this Stream targetStream, Action writeProc, CancellationToken ct) { using MemoryStream ms = new MemoryStream(); await targetStream.WriteProcToStreamAsync(ms, writeProc, ct); } } internal class StringBuilderPool { private readonly ConcurrentQueue pool = new ConcurrentQueue(); public static StringBuilderPool Instance { get; } = new StringBuilderPool(); public StringBuilder Rent() { if (!pool.TryDequeue(out var result)) { return new StringBuilder(); } return result; } public void Return(StringBuilder builder) { builder.Clear(); pool.Enqueue(builder); } } } namespace ICSharpCode.SharpZipLib.Checksum { public sealed class Adler32 : IChecksum { private static readonly uint BASE = 65521u; private uint checkValue; public long Value => checkValue; public Adler32() { Reset(); } public void Reset() { checkValue = 1u; } public void Update(int bval) { uint num = checkValue & 0xFFFF; uint num2 = checkValue >> 16; num = (uint)((int)num + (bval & 0xFF)) % BASE; num2 = (num + num2) % BASE; checkValue = (num2 << 16) + num; } public void Update(byte[] buffer) { if (buffer == null) { throw new ArgumentNullException("buffer"); } Update(new ArraySegment(buffer, 0, buffer.Length)); } public void Update(ArraySegment segment) { uint num = checkValue & 0xFFFF; uint num2 = checkValue >> 16; int num3 = segment.Count; int offset = segment.Offset; while (num3 > 0) { int num4 = 3800; if (num4 > num3) { num4 = num3; } num3 -= num4; while (--num4 >= 0) { num += (uint)(segment.Array[offset++] & 0xFF); num2 += num; } num %= BASE; num2 %= BASE; } checkValue = (num2 << 16) | num; } } public sealed class BZip2Crc : IChecksum { private const uint crcInit = uint.MaxValue; private static readonly uint[] crcTable = CrcUtilities.GenerateSlicingLookupTable(79764919u, isReversed: false); private uint checkValue; public long Value => ~checkValue; public BZip2Crc() { Reset(); } public void Reset() { checkValue = uint.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update(int bval) { checkValue = crcTable[(byte)(((checkValue >> 24) & 0xFF) ^ bval)] ^ (checkValue << 8); } public void Update(byte[] buffer) { if (buffer == null) { throw new ArgumentNullException("buffer"); } Update(buffer, 0, buffer.Length); } public void Update(ArraySegment segment) { Update(segment.Array, segment.Offset, segment.Count); } private void Update(byte[] data, int offset, int count) { int num = count % 16; int num2 = offset + count - num; while (offset != num2) { checkValue = CrcUtilities.UpdateDataForNormalPoly(data, offset, crcTable, checkValue); offset += 16; } if (num != 0) { SlowUpdateLoop(data, offset, num2 + num); } } [MethodImpl(MethodImplOptions.NoInlining)] private void SlowUpdateLoop(byte[] data, int offset, int end) { while (offset != end) { Update(data[offset++]); } } } public sealed class Crc32 : IChecksum { private static readonly uint crcInit = uint.MaxValue; private static readonly uint crcXor = uint.MaxValue; private static readonly uint[] crcTable = CrcUtilities.GenerateSlicingLookupTable(3988292384u, isReversed: true); private uint checkValue; public long Value => checkValue ^ crcXor; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static uint ComputeCrc32(uint oldCrc, byte bval) { return crcTable[(oldCrc ^ bval) & 0xFF] ^ (oldCrc >> 8); } public Crc32() { Reset(); } public void Reset() { checkValue = crcInit; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update(int bval) { checkValue = crcTable[(checkValue ^ bval) & 0xFF] ^ (checkValue >> 8); } public void Update(byte[] buffer) { if (buffer == null) { throw new ArgumentNullException("buffer"); } Update(buffer, 0, buffer.Length); } public void Update(ArraySegment segment) { Update(segment.Array, segment.Offset, segment.Count); } private void Update(byte[] data, int offset, int count) { int num = count % 16; int num2 = offset + count - num; while (offset != num2) { checkValue = CrcUtilities.UpdateDataForReversedPoly(data, offset, crcTable, checkValue); offset += 16; } if (num != 0) { SlowUpdateLoop(data, offset, num2 + num); } } [MethodImpl(MethodImplOptions.NoInlining)] private void SlowUpdateLoop(byte[] data, int offset, int end) { while (offset != end) { Update(data[offset++]); } } } internal static class CrcUtilities { internal const int SlicingDegree = 16; internal static uint[] GenerateSlicingLookupTable(uint polynomial, bool isReversed) { uint[] array = new uint[4096]; uint num = (isReversed ? 1u : 2147483648u); for (int i = 0; i < 256; i++) { uint num2 = (uint)(isReversed ? i : (i << 24)); for (int j = 0; j < 16; j++) { for (int k = 0; k < 8; k++) { num2 = ((!isReversed) ? (((num2 & num) != 0) ? (polynomial ^ (num2 << 1)) : (num2 << 1)) : (((num2 & num) == 1) ? (polynomial ^ (num2 >> 1)) : (num2 >> 1))); } array[256 * j + i] = num2; } } return array; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static uint UpdateDataForNormalPoly(byte[] input, int offset, uint[] crcTable, uint checkValue) { byte x = (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, x, x2, x3, x4); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static uint UpdateDataForReversedPoly(byte[] input, int offset, uint[] crcTable, uint checkValue) { byte x = (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, x, x2, x3, x4); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint UpdateDataCommon(byte[] input, int offset, uint[] crcTable, byte x1, byte x2, byte x3, byte x4) { uint num = crcTable[x1 + 3840] ^ crcTable[x2 + 3584]; uint num2 = crcTable[x3 + 3328] ^ crcTable[x4 + 3072]; uint num3 = crcTable[input[offset + 4] + 2816] ^ crcTable[input[offset + 5] + 2560]; num ^= crcTable[input[offset + 9] + 1536]; uint num4 = num3 ^ crcTable[input[offset + 6] + 2304] ^ crcTable[input[offset + 7] + 2048] ^ crcTable[input[offset + 8] + 1792]; num2 ^= crcTable[input[offset + 13] + 512]; return num4 ^ crcTable[input[offset + 10] + 1280] ^ crcTable[input[offset + 11] + 1024] ^ crcTable[input[offset + 12] + 768] ^ num ^ crcTable[input[offset + 14] + 256] ^ crcTable[input[offset + 15]] ^ num2; } } public interface IChecksum { long Value { get; } void Reset(); void Update(int bval); void Update(byte[] buffer); void Update(ArraySegment segment); } } namespace ICSharpCode.SharpZipLib.BZip2 { public static class BZip2 { public static void Decompress(Stream inStream, Stream outStream, bool isStreamOwner) { if (inStream == null) { throw new ArgumentNullException("inStream"); } if (outStream == null) { throw new ArgumentNullException("outStream"); } try { using BZip2InputStream bZip2InputStream = new BZip2InputStream(inStream); bZip2InputStream.IsStreamOwner = isStreamOwner; StreamUtils.Copy(bZip2InputStream, outStream, new byte[4096]); } finally { if (isStreamOwner) { outStream.Dispose(); } } } public static void Compress(Stream inStream, Stream outStream, bool isStreamOwner, int level) { if (inStream == null) { throw new ArgumentNullException("inStream"); } if (outStream == null) { throw new ArgumentNullException("outStream"); } try { using BZip2OutputStream bZip2OutputStream = new BZip2OutputStream(outStream, level); bZip2OutputStream.IsStreamOwner = isStreamOwner; StreamUtils.Copy(inStream, bZip2OutputStream, new byte[4096]); } finally { if (isStreamOwner) { inStream.Dispose(); } } } } internal static class BZip2Constants { public static readonly int[] RandomNumbers = new int[512] { 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 }; public const int BaseBlockSize = 100000; public const int MaximumAlphaSize = 258; public const int MaximumCodeLength = 23; public const int RunA = 0; public const int RunB = 1; public const int GroupCount = 6; public const int GroupSize = 50; public const int NumberOfIterations = 4; public const int MaximumSelectors = 18002; public const int OvershootBytes = 20; } [Serializable] public class BZip2Exception : SharpZipBaseException { public BZip2Exception() { } public BZip2Exception(string message) : base(message) { } public BZip2Exception(string message, Exception innerException) : base(message, innerException) { } protected BZip2Exception(SerializationInfo info, StreamingContext context) : base(info, context) { } } public class BZip2InputStream : Stream { 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; private int last; private int origPtr; 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[18002]; private byte[] selectorMtf = new byte[18002]; private int[] tt; private byte[] ll8; private int[] unzftab = new int[256]; private int[][] limit = new int[6][]; private int[][] baseArray = new int[6][]; private int[][] perm = new int[6][]; private int[] minLens = new int[6]; private readonly Stream baseStream; private bool streamEnd; private int currentChar = -1; private int currentState = 1; private int storedBlockCRC; private int storedCombinedCRC; private int computedBlockCRC; private uint computedCombinedCRC; private int count; private int chPrev; private int ch2; private int tPos; private int rNToGo; private int rTPos; private int i2; private int j2; private byte z; public bool IsStreamOwner { get; set; } = true; public override bool CanRead => baseStream.CanRead; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length => baseStream.Length; public override long Position { get { return baseStream.Position; } set { throw new NotSupportedException("BZip2InputStream position cannot be set"); } } public BZip2InputStream(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } for (int i = 0; i < 6; i++) { limit[i] = new int[258]; baseArray[i] = new int[258]; perm[i] = new int[258]; } baseStream = stream; bsLive = 0; bsBuff = 0; Initialize(); InitBlock(); SetupBlock(); } public override void Flush() { baseStream.Flush(); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException("BZip2InputStream Seek not supported"); } public override void SetLength(long value) { throw new NotSupportedException("BZip2InputStream SetLength not supported"); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException("BZip2InputStream Write not supported"); } public override void WriteByte(byte value) { throw new NotSupportedException("BZip2InputStream WriteByte not supported"); } public override int Read(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException("buffer"); } for (int i = 0; i < count; i++) { int num = ReadByte(); if (num == -1) { return i; } buffer[offset + i] = (byte)num; } return count; } protected override void Dispose(bool disposing) { if (disposing && IsStreamOwner) { baseStream.Dispose(); } } public override int ReadByte() { if (streamEnd) { return -1; } int result = currentChar; switch (currentState) { case 3: SetupRandPartB(); break; case 4: SetupRandPartC(); break; case 6: SetupNoRandPartB(); break; case 7: SetupNoRandPartC(); break; } return result; } 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 num = BsGetUChar(); char c = BsGetUChar(); char c2 = BsGetUChar(); char c3 = BsGetUChar(); if (num != 'B' || c != 'Z' || c2 != 'h' || c3 < '1' || c3 > '9') { streamEnd = true; return; } SetDecompressStructureSizes(c3 - 48); computedCombinedCRC = 0u; } private void InitBlock() { char c = BsGetUChar(); char c2 = BsGetUChar(); char c3 = BsGetUChar(); char c4 = BsGetUChar(); char c5 = BsGetUChar(); char c6 = BsGetUChar(); if (c == '\u0017' && c2 == 'r' && c3 == 'E' && c4 == '8' && c5 == 'P' && c6 == '\u0090') { Complete(); return; } if (c != '1' || c2 != 'A' || c3 != 'Y' || c4 != '&' || c5 != 'S' || c6 != 'Y') { BadBlockHeader(); streamEnd = true; return; } storedBlockCRC = BsGetInt32(); blockRandomised = BsR(1) == 1; GetAndMoveToFrontDecode(); mCrc.Reset(); currentState = 1; } private void EndBlock() { computedBlockCRC = (int)mCrc.Value; if (storedBlockCRC != computedBlockCRC) { CrcError(); } computedCombinedCRC = ((computedCombinedCRC << 1) & 0xFFFFFFFFu) | (computedCombinedCRC >> 31); computedCombinedCRC ^= (uint)computedBlockCRC; } private void Complete() { storedCombinedCRC = BsGetInt32(); if (storedCombinedCRC != (int)computedCombinedCRC) { CrcError(); } streamEnd = true; } private void FillBuffer() { int num = 0; try { num = baseStream.ReadByte(); } catch (Exception) { CompressedStreamEOF(); } if (num == -1) { CompressedStreamEOF(); } bsBuff = (bsBuff << 8) | (num & 0xFF); bsLive += 8; } private int BsR(int n) { while (bsLive < n) { FillBuffer(); } int result = (bsBuff >> bsLive - n) & ((1 << n) - 1); bsLive -= n; return result; } private char BsGetUChar() { return (char)BsR(8); } private int BsGetIntVS(int numBits) { return BsR(numBits); } private int BsGetInt32() { return (((((BsR(8) << 8) | BsR(8)) << 8) | BsR(8)) << 8) | BsR(8); } private void RecvDecodingTables() { char[][] array = new char[6][]; for (int i = 0; i < 6; i++) { array[i] = new char[258]; } bool[] array2 = new bool[16]; for (int j = 0; j < 16; j++) { array2[j] = BsR(1) == 1; } for (int k = 0; k < 16; k++) { if (array2[k]) { for (int l = 0; l < 16; l++) { inUse[k * 16 + l] = BsR(1) == 1; } } else { for (int m = 0; m < 16; m++) { inUse[k * 16 + m] = false; } } } MakeMaps(); int num = nInUse + 2; int num2 = BsR(3); int num3 = BsR(15); for (int n = 0; n < num3; n++) { int num4 = 0; while (BsR(1) == 1) { num4++; } selectorMtf[n] = (byte)num4; } byte[] array3 = new byte[6]; for (int num5 = 0; num5 < num2; num5++) { array3[num5] = (byte)num5; } for (int num6 = 0; num6 < num3; num6++) { int num7 = selectorMtf[num6]; byte b = array3[num7]; while (num7 > 0) { array3[num7] = array3[num7 - 1]; num7--; } array3[0] = b; selector[num6] = b; } for (int num8 = 0; num8 < num2; num8++) { int num9 = BsR(5); for (int num10 = 0; num10 < num; num10++) { while (BsR(1) == 1) { num9 = ((BsR(1) != 0) ? (num9 - 1) : (num9 + 1)); } array[num8][num10] = (char)num9; } } for (int num11 = 0; num11 < num2; num11++) { int num12 = 32; int num13 = 0; for (int num14 = 0; num14 < num; num14++) { num13 = Math.Max(num13, array[num11][num14]); num12 = Math.Min(num12, array[num11][num14]); } HbCreateDecodeTables(limit[num11], baseArray[num11], perm[num11], array[num11], num12, num13, num); minLens[num11] = num12; } } private void GetAndMoveToFrontDecode() { byte[] array = new byte[256]; int num = 100000 * blockSize100k; origPtr = BsGetIntVS(24); RecvDecodingTables(); int num2 = nInUse + 1; int num3 = -1; int num4 = 0; for (int i = 0; i <= 255; i++) { unzftab[i] = 0; } for (int j = 0; j <= 255; j++) { array[j] = (byte)j; } last = -1; if (num4 == 0) { num3++; num4 = 50; } num4--; int num5 = selector[num3]; int num6 = minLens[num5]; int num7 = BsR(num6); while (num7 > limit[num5][num6]) { if (num6 > 20) { throw new BZip2Exception("Bzip data error"); } num6++; while (bsLive < 1) { FillBuffer(); } int num8 = (bsBuff >> bsLive - 1) & 1; bsLive--; num7 = (num7 << 1) | num8; } if (num7 - baseArray[num5][num6] < 0 || num7 - baseArray[num5][num6] >= 258) { throw new BZip2Exception("Bzip data error"); } int num9 = perm[num5][num7 - baseArray[num5][num6]]; while (num9 != num2) { if (num9 == 0 || num9 == 1) { int num10 = -1; int num11 = 1; do { switch (num9) { case 0: num10 += num11; break; case 1: num10 += 2 * num11; break; } num11 <<= 1; if (num4 == 0) { num3++; num4 = 50; } num4--; num5 = selector[num3]; num6 = minLens[num5]; num7 = BsR(num6); while (num7 > limit[num5][num6]) { num6++; while (bsLive < 1) { FillBuffer(); } int num8 = (bsBuff >> bsLive - 1) & 1; bsLive--; num7 = (num7 << 1) | num8; } num9 = perm[num5][num7 - baseArray[num5][num6]]; } while (num9 == 0 || num9 == 1); num10++; byte b = seqToUnseq[array[0]]; unzftab[b] += num10; while (num10 > 0) { last++; ll8[last] = b; num10--; } if (last >= num) { BlockOverrun(); } continue; } last++; if (last >= num) { BlockOverrun(); } byte b2 = array[num9 - 1]; unzftab[seqToUnseq[b2]]++; ll8[last] = seqToUnseq[b2]; int num12 = num9 - 1; while (num12 > 0) { array[num12] = array[--num12]; } array[0] = b2; if (num4 == 0) { num3++; num4 = 50; } num4--; num5 = selector[num3]; num6 = minLens[num5]; num7 = BsR(num6); while (num7 > limit[num5][num6]) { num6++; while (bsLive < 1) { FillBuffer(); } int num8 = (bsBuff >> bsLive - 1) & 1; bsLive--; num7 = (num7 << 1) | num8; } num9 = perm[num5][num7 - baseArray[num5][num6]]; } } private void SetupBlock() { int[] array = new int[257]; array[0] = 0; Array.Copy(unzftab, 0, array, 1, 256); for (int i = 1; i <= 256; i++) { array[i] += array[i - 1]; } for (int j = 0; j <= last; j++) { byte b = ll8[j]; tt[array[b]] = j; array[b]++; } array = null; tPos = tt[origPtr]; count = 0; i2 = 0; ch2 = 256; 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 ^= ((rNToGo == 1) ? 1 : 0); i2++; currentChar = ch2; currentState = 3; 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 = 6; mCrc.Update(ch2); } else { EndBlock(); InitBlock(); SetupBlock(); } } private void SetupRandPartB() { if (ch2 != chPrev) { currentState = 2; count = 1; SetupRandPartA(); return; } 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 ^= ((rNToGo == 1) ? ((byte)1) : ((byte)0)); j2 = 0; currentState = 4; SetupRandPartC(); } else { currentState = 2; SetupRandPartA(); } } private void SetupRandPartC() { if (j2 < z) { currentChar = ch2; mCrc.Update(ch2); j2++; } else { currentState = 2; i2++; count = 0; SetupRandPartA(); } } private void SetupNoRandPartB() { if (ch2 != chPrev) { currentState = 5; count = 1; SetupNoRandPartA(); return; } count++; if (count >= 4) { z = ll8[tPos]; tPos = tt[tPos]; currentState = 7; j2 = 0; SetupNoRandPartC(); } else { currentState = 5; SetupNoRandPartA(); } } private void SetupNoRandPartC() { if (j2 < z) { currentChar = ch2; mCrc.Update(ch2); j2++; } else { currentState = 5; 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) { int num = 100000 * newSize100k; ll8 = new byte[num]; tt = new int[num]; } } 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 num = 0; for (int i = minLen; i <= maxLen; i++) { for (int j = 0; j < alphaSize; j++) { if (length[j] == i) { perm[num] = j; num++; } } } for (int k = 0; k < 23; k++) { baseArray[k] = 0; } for (int l = 0; l < alphaSize; l++) { baseArray[length[l] + 1]++; } for (int m = 1; m < 23; m++) { baseArray[m] += baseArray[m - 1]; } for (int n = 0; n < 23; n++) { limit[n] = 0; } int num2 = 0; for (int num3 = minLen; num3 <= maxLen; num3++) { num2 += baseArray[num3 + 1] - baseArray[num3]; limit[num3] = num2 - 1; num2 <<= 1; } for (int num4 = minLen + 1; num4 <= maxLen; num4++) { baseArray[num4] = (limit[num4 - 1] + 1 << 1) - baseArray[num4]; } } } public class BZip2OutputStream : Stream { private struct StackElement { public int ll; public int hh; public int dd; } private const int SETMASK = 2097152; private const int CLEARMASK = -2097153; private const int GREATER_ICOST = 15; private const int LESSER_ICOST = 0; private const int SMALL_THRESH = 20; private const int DEPTH_THRESH = 10; private const int QSORT_STACK_SIZE = 1000; private readonly int[] increments = new int[14] { 1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524, 88573, 265720, 797161, 2391484 }; private int last; private int origPtr; 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[18002]; private char[] selectorMtf = new char[18002]; private byte[] block; private int[] quadrant; private int[] zptr; private short[] szptr; private int[] ftab; private int nMTF; private int[] mtfFreq = new int[258]; 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; private uint combinedCRC; private int allowableBlockSize; private readonly Stream baseStream; private bool disposed_; public bool IsStreamOwner { get; set; } = true; public override bool CanRead => false; public override bool CanSeek => false; public override bool CanWrite => baseStream.CanWrite; public override long Length => baseStream.Length; public override long Position { get { return baseStream.Position; } set { throw new NotSupportedException("BZip2OutputStream position cannot be set"); } } public int BytesWritten => bytesOut; public BZip2OutputStream(Stream stream) : this(stream, 9) { } public BZip2OutputStream(Stream stream, int blockSize) { if (stream == null) { throw new ArgumentNullException("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(); } ~BZip2OutputStream() { Dispose(disposing: false); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException("BZip2OutputStream Seek not supported"); } public override void SetLength(long value) { throw new NotSupportedException("BZip2OutputStream SetLength not supported"); } public override int ReadByte() { throw new NotSupportedException("BZip2OutputStream ReadByte not supported"); } public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException("BZip2OutputStream Read not supported"); } public override void Write(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0) { throw new ArgumentOutOfRangeException("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]); } } public override void WriteByte(byte value) { int num = (256 + value) % 256; if (currentChar != -1) { if (currentChar == num) { runLength++; if (runLength > 254) { WriteRun(); currentChar = -1; runLength = 0; } } else { WriteRun(); runLength = 1; currentChar = num; } } else { currentChar = num; 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++; } } } 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(); } } protected override 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 && IsStreamOwner) { baseStream.Dispose(); } } } catch { } } public override void Flush() { baseStream.Flush(); } private void Initialize() { bytesOut = 0; nBlocksRandomised = 0; BsPutUChar(66); BsPutUChar(90); BsPutUChar(104); BsPutUChar(48 + blockSize100k); combinedCRC = 0u; } private void InitBlock() { mCrc.Reset(); last = -1; for (int i = 0; i < 256; i++) { inUse[i] = false; } allowableBlockSize = 100000 * blockSize100k - 20; } private void EndBlock() { if (last >= 0) { blockCRC = (uint)mCrc.Value; combinedCRC = (combinedCRC << 1) | (combinedCRC >> 31); combinedCRC ^= blockCRC; DoReversibleTransformation(); BsPutUChar(49); BsPutUChar(65); BsPutUChar(89); BsPutUChar(38); BsPutUChar(83); BsPutUChar(89); BsPutint((int)blockCRC); if (blockRandomised) { BsW(1, 1); nBlocksRandomised++; } else { BsW(1, 0); } MoveToFrontCodeAndSend(); } } private void EndCompression() { BsPutUChar(23); BsPutUChar(114); BsPutUChar(69); BsPutUChar(56); BsPutUChar(80); BsPutUChar(144); BsPutint((int)combinedCRC); BsFinishedWithStream(); } private void BsFinishedWithStream() { while (bsLive > 0) { int num = bsBuff >> 24; baseStream.WriteByte((byte)num); bsBuff <<= 8; bsLive -= 8; bytesOut++; } } private void BsW(int n, int v) { while (bsLive >= 8) { int num = bsBuff >> 24; baseStream.WriteByte((byte)num); 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[][] array = new char[6][]; for (int i = 0; i < 6; i++) { array[i] = new char[258]; } int num = 0; int num2 = nInUse + 2; for (int j = 0; j < 6; j++) { for (int k = 0; k < num2; k++) { array[j][k] = '\u000f'; } } if (nMTF <= 0) { Panic(); } int num3 = ((nMTF < 200) ? 2 : ((nMTF < 600) ? 3 : ((nMTF < 1200) ? 4 : ((nMTF >= 2400) ? 6 : 5)))); int num4 = num3; int num5 = nMTF; int num6 = 0; while (num4 > 0) { int num7 = num5 / num4; int l = 0; int num8; for (num8 = num6 - 1; l < num7; l += mtfFreq[num8]) { if (num8 >= num2 - 1) { break; } num8++; } if (num8 > num6 && num4 != num3 && num4 != 1 && (num3 - num4) % 2 == 1) { l -= mtfFreq[num8]; num8--; } for (int m = 0; m < num2; m++) { if (m >= num6 && m <= num8) { array[num4 - 1][m] = '\0'; } else { array[num4 - 1][m] = '\u000f'; } } num4--; num6 = num8 + 1; num5 -= l; } int[][] array2 = new int[6][]; for (int n = 0; n < 6; n++) { array2[n] = new int[258]; } int[] array3 = new int[6]; short[] array4 = new short[6]; for (int num9 = 0; num9 < 4; num9++) { for (int num10 = 0; num10 < num3; num10++) { array3[num10] = 0; } for (int num11 = 0; num11 < num3; num11++) { for (int num12 = 0; num12 < num2; num12++) { array2[num11][num12] = 0; } } num = 0; int num13 = 0; num6 = 0; while (num6 < nMTF) { int num8 = num6 + 50 - 1; if (num8 >= nMTF) { num8 = nMTF - 1; } for (int num14 = 0; num14 < num3; num14++) { array4[num14] = 0; } if (num3 == 6) { short num16; short num17; short num18; short num19; short num20; short num15 = (num16 = (num17 = (num18 = (num19 = (num20 = 0))))); for (int num21 = num6; num21 <= num8; num21++) { short num22 = szptr[num21]; num15 += (short)array[0][num22]; num16 += (short)array[1][num22]; num17 += (short)array[2][num22]; num18 += (short)array[3][num22]; num19 += (short)array[4][num22]; num20 += (short)array[5][num22]; } array4[0] = num15; array4[1] = num16; array4[2] = num17; array4[3] = num18; array4[4] = num19; array4[5] = num20; } else { for (int num23 = num6; num23 <= num8; num23++) { short num24 = szptr[num23]; for (int num25 = 0; num25 < num3; num25++) { array4[num25] += (short)array[num25][num24]; } } } int num26 = 999999999; int num27 = -1; for (int num28 = 0; num28 < num3; num28++) { if (array4[num28] < num26) { num26 = array4[num28]; num27 = num28; } } num13 += num26; array3[num27]++; selector[num] = (char)num27; num++; for (int num29 = num6; num29 <= num8; num29++) { array2[num27][szptr[num29]]++; } num6 = num8 + 1; } for (int num30 = 0; num30 < num3; num30++) { HbMakeCodeLengths(array[num30], array2[num30], num2, 20); } } array2 = null; array3 = null; array4 = null; if (num3 >= 8) { Panic(); } if (num >= 32768 || num > 18002) { Panic(); } char[] array5 = new char[6]; for (int num31 = 0; num31 < num3; num31++) { array5[num31] = (char)num31; } for (int num32 = 0; num32 < num; num32++) { char c = selector[num32]; int num33 = 0; char c2 = array5[num33]; while (c != c2) { num33++; char c3 = c2; c2 = array5[num33]; array5[num33] = c3; } array5[0] = c2; selectorMtf[num32] = (char)num33; } int[][] array6 = new int[6][]; for (int num34 = 0; num34 < 6; num34++) { array6[num34] = new int[258]; } for (int num35 = 0; num35 < num3; num35++) { int num36 = 32; int num37 = 0; for (int num38 = 0; num38 < num2; num38++) { if (array[num35][num38] > num37) { num37 = array[num35][num38]; } if (array[num35][num38] < num36) { num36 = array[num35][num38]; } } if (num37 > 20) { Panic(); } if (num36 < 1) { Panic(); } HbAssignCodes(array6[num35], array[num35], num36, num37, num2); } bool[] array7 = new bool[16]; for (int num39 = 0; num39 < 16; num39++) { array7[num39] = false; for (int num40 = 0; num40 < 16; num40++) { if (inUse[num39 * 16 + num40]) { array7[num39] = true; } } } for (int num41 = 0; num41 < 16; num41++) { if (array7[num41]) { BsW(1, 1); } else { BsW(1, 0); } } for (int num42 = 0; num42 < 16; num42++) { if (!array7[num42]) { continue; } for (int num43 = 0; num43 < 16; num43++) { if (inUse[num42 * 16 + num43]) { BsW(1, 1); } else { BsW(1, 0); } } } BsW(3, num3); BsW(15, num); for (int num44 = 0; num44 < num; num44++) { for (int num45 = 0; num45 < selectorMtf[num44]; num45++) { BsW(1, 1); } BsW(1, 0); } for (int num46 = 0; num46 < num3; num46++) { int num47 = array[num46][0]; BsW(5, num47); for (int num48 = 0; num48 < num2; num48++) { for (; num47 < array[num46][num48]; num47++) { BsW(2, 2); } while (num47 > array[num46][num48]) { BsW(2, 3); num47--; } BsW(1, 0); } } int num49 = 0; num6 = 0; while (num6 < nMTF) { int num8 = num6 + 50 - 1; if (num8 >= nMTF) { num8 = nMTF - 1; } for (int num50 = num6; num50 <= num8; num50++) { BsW(array[(uint)selector[num49]][szptr[num50]], array6[(uint)selector[num49]][szptr[num50]]); } num6 = num8 + 1; num49++; } if (num49 != num) { Panic(); } } private void MoveToFrontCodeAndSend() { BsPutIntVS(24, origPtr); GenerateMTFValues(); SendMTFValues(); } private void SimpleSort(int lo, int hi, int d) { int num = hi - lo + 1; if (num < 2) { return; } int i; for (i = 0; increments[i] < num; i++) { } for (i--; i >= 0; i--) { int num2 = increments[i]; int num3 = lo + num2; while (num3 <= hi) { int num4 = zptr[num3]; int num5 = num3; while (FullGtU(zptr[num5 - num2] + d, num4 + d)) { zptr[num5] = zptr[num5 - num2]; num5 -= num2; if (num5 <= lo + num2 - 1) { break; } } zptr[num5] = num4; num3++; if (num3 > hi) { break; } num4 = zptr[num3]; num5 = num3; while (FullGtU(zptr[num5 - num2] + d, num4 + d)) { zptr[num5] = zptr[num5 - num2]; num5 -= num2; if (num5 <= lo + num2 - 1) { break; } } zptr[num5] = num4; num3++; if (num3 > hi) { break; } num4 = zptr[num3]; num5 = num3; while (FullGtU(zptr[num5 - num2] + d, num4 + d)) { zptr[num5] = zptr[num5 - num2]; num5 -= num2; if (num5 <= lo + num2 - 1) { break; } } zptr[num5] = num4; num3++; if (workDone > workLimit && firstAttempt) { return; } } } } private void Vswap(int p1, int p2, int n) { int num = 0; while (n > 0) { num = zptr[p1]; zptr[p1] = zptr[p2]; zptr[p2] = num; p1++; p2++; n--; } } private void QSort3(int loSt, int hiSt, int dSt) { StackElement[] array = new StackElement[1000]; int num = 0; array[num].ll = loSt; array[num].hh = hiSt; array[num].dd = dSt; num++; while (num > 0) { if (num >= 1000) { Panic(); } num--; int ll = array[num].ll; int hh = array[num].hh; int dd = array[num].dd; if (hh - ll < 20 || dd > 10) { SimpleSort(ll, hh, dd); if (workDone > workLimit && firstAttempt) { break; } continue; } int num2 = Med3(block[zptr[ll] + dd + 1], block[zptr[hh] + dd + 1], block[zptr[ll + hh >> 1] + dd + 1]); int num4; int num3 = (num4 = ll); int num6; int num5 = (num6 = hh); int num7; while (true) { if (num3 <= num5) { num7 = block[zptr[num3] + dd + 1] - num2; if (num7 == 0) { int num8 = zptr[num3]; zptr[num3] = zptr[num4]; zptr[num4] = num8; num4++; num3++; continue; } if (num7 <= 0) { num3++; continue; } } while (num3 <= num5) { num7 = block[zptr[num5] + dd + 1] - num2; if (num7 == 0) { int num9 = zptr[num5]; zptr[num5] = zptr[num6]; zptr[num6] = num9; num6--; num5--; } else { if (num7 < 0) { break; } num5--; } } if (num3 > num5) { break; } int num10 = zptr[num3]; zptr[num3] = zptr[num5]; zptr[num5] = num10; num3++; num5--; } if (num6 < num4) { array[num].ll = ll; array[num].hh = hh; array[num].dd = dd + 1; num++; continue; } num7 = ((num4 - ll < num3 - num4) ? (num4 - ll) : (num3 - num4)); Vswap(ll, num3 - num7, num7); int num11 = ((hh - num6 < num6 - num5) ? (hh - num6) : (num6 - num5)); Vswap(num3, hh - num11 + 1, num11); num7 = ll + num3 - num4 - 1; num11 = hh - (num6 - num5) + 1; array[num].ll = ll; array[num].hh = num7; array[num].dd = dd; num++; array[num].ll = num7 + 1; array[num].hh = num11 - 1; array[num].dd = dd + 1; num++; array[num].ll = num11; array[num].hh = hh; array[num].dd = dd; num++; } } private void MainSort() { int[] array = new int[256]; int[] array2 = new int[256]; bool[] array3 = new bool[256]; for (int i = 0; i < 20; i++) { block[last + i + 2] = block[i % (last + 1) + 1]; } for (int i = 0; i <= last + 20; i++) { quadrant[i] = 0; } block[0] = block[last + 1]; if (last < 4000) { for (int i = 0; i <= last; i++) { zptr[i] = i; } firstAttempt = false; workDone = (workLimit = 0); SimpleSort(0, last, 0); return; } int num = 0; for (int i = 0; i <= 255; i++) { array3[i] = false; } for (int i = 0; i <= 65536; i++) { ftab[i] = 0; } int num2 = block[0]; for (int i = 0; i <= last; i++) { int num3 = block[i + 1]; ftab[(num2 << 8) + num3]++; num2 = num3; } for (int i = 1; i <= 65536; i++) { ftab[i] += ftab[i - 1]; } num2 = block[1]; int num4; for (int i = 0; i < last; i++) { int num3 = block[i + 2]; num4 = (num2 << 8) + num3; num2 = num3; ftab[num4]--; zptr[ftab[num4]] = i; } num4 = (block[last + 1] << 8) + block[1]; ftab[num4]--; zptr[ftab[num4]] = last; for (int i = 0; i <= 255; i++) { array[i] = i; } int num5 = 1; do { num5 = 3 * num5 + 1; } while (num5 <= 256); do { num5 /= 3; for (int i = num5; i <= 255; i++) { int num6 = array[i]; num4 = i; while (ftab[array[num4 - num5] + 1 << 8] - ftab[array[num4 - num5] << 8] > ftab[num6 + 1 << 8] - ftab[num6 << 8]) { array[num4] = array[num4 - num5]; num4 -= num5; if (num4 <= num5 - 1) { break; } } array[num4] = num6; } } while (num5 != 1); for (int i = 0; i <= 255; i++) { int num7 = array[i]; for (num4 = 0; num4 <= 255; num4++) { int num8 = (num7 << 8) + num4; if ((ftab[num8] & 0x200000) == 2097152) { continue; } int num9 = ftab[num8] & -2097153; int num10 = (ftab[num8 + 1] & -2097153) - 1; if (num10 > num9) { QSort3(num9, num10, 2); num += num10 - num9 + 1; if (workDone > workLimit && firstAttempt) { return; } } ftab[num8] |= 2097152; } array3[num7] = true; if (i < 255) { int num11 = ftab[num7 << 8] & -2097153; int num12 = (ftab[num7 + 1 << 8] & -2097153) - num11; int j; for (j = 0; num12 >> j > 65534; j++) { } for (num4 = 0; num4 < num12; num4++) { int num13 = zptr[num11 + num4]; int num14 = num4 >> j; quadrant[num13] = num14; if (num13 < 20) { quadrant[num13 + last + 1] = num14; } } if (num12 - 1 >> j > 65535) { Panic(); } } for (num4 = 0; num4 <= 255; num4++) { array2[num4] = ftab[(num4 << 8) + num7] & -2097153; } for (num4 = ftab[num7 << 8] & -2097153; num4 < (ftab[num7 + 1 << 8] & -2097153); num4++) { num2 = block[zptr[num4]]; if (!array3[num2]) { zptr[array2[num2]] = ((zptr[num4] == 0) ? last : (zptr[num4] - 1)); array2[num2]++; } } for (num4 = 0; num4 <= 255; num4++) { ftab[(num4 << 8) + num7] |= 2097152; } } } private void RandomiseBlock() { int num = 0; int num2 = 0; for (int i = 0; i < 256; i++) { inUse[i] = false; } for (int i = 0; i <= last; i++) { if (num == 0) { num = BZip2Constants.RandomNumbers[num2]; num2++; if (num2 == 512) { num2 = 0; } } num--; block[i + 1] ^= ((num == 1) ? ((byte)1) : ((byte)0)); block[i + 1] &= byte.MaxValue; 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) { byte b = block[i1 + 1]; byte b2 = block[i2 + 1]; if (b != b2) { return b > b2; } i1++; i2++; b = block[i1 + 1]; b2 = block[i2 + 1]; if (b != b2) { return b > b2; } i1++; i2++; b = block[i1 + 1]; b2 = block[i2 + 1]; if (b != b2) { return b > b2; } i1++; i2++; b = block[i1 + 1]; b2 = block[i2 + 1]; if (b != b2) { return b > b2; } i1++; i2++; b = block[i1 + 1]; b2 = block[i2 + 1]; if (b != b2) { return b > b2; } i1++; i2++; b = block[i1 + 1]; b2 = block[i2 + 1]; if (b != b2) { return b > b2; } i1++; i2++; int num = last + 1; do { b = block[i1 + 1]; b2 = block[i2 + 1]; if (b != b2) { return b > b2; } int num2 = quadrant[i1]; int num3 = quadrant[i2]; if (num2 != num3) { return num2 > num3; } i1++; i2++; b = block[i1 + 1]; b2 = block[i2 + 1]; if (b != b2) { return b > b2; } num2 = quadrant[i1]; num3 = quadrant[i2]; if (num2 != num3) { return num2 > num3; } i1++; i2++; b = block[i1 + 1]; b2 = block[i2 + 1]; if (b != b2) { return b > b2; } num2 = quadrant[i1]; num3 = quadrant[i2]; if (num2 != num3) { return num2 > num3; } i1++; i2++; b = block[i1 + 1]; b2 = block[i2 + 1]; if (b != b2) { return b > b2; } num2 = quadrant[i1]; num3 = quadrant[i2]; if (num2 != num3) { return num2 > num3; } i1++; i2++; if (i1 > last) { i1 -= last; i1--; } if (i2 > last) { i2 -= last; i2--; } num -= 4; workDone++; } while (num >= 0); return false; } private void AllocateCompressStructures() { int num = 100000 * blockSize100k; block = new byte[num + 1 + 20]; quadrant = new int[num + 20]; zptr = new int[num]; ftab = new int[65537]; if (block != null && quadrant != null && zptr != null) { _ = ftab; } szptr = new short[2 * num]; } private void GenerateMTFValues() { char[] array = new char[256]; MakeMaps(); int num = nInUse + 1; for (int i = 0; i <= num; i++) { mtfFreq[i] = 0; } int num2 = 0; int num3 = 0; for (int i = 0; i < nInUse; i++) { array[i] = (char)i; } for (int i = 0; i <= last; i++) { char c = unseqToSeq[block[zptr[i]]]; int num4 = 0; char c2 = array[num4]; while (c != c2) { num4++; char c3 = c2; c2 = array[num4]; array[num4] = c3; } array[0] = c2; if (num4 == 0) { num3++; continue; } if (num3 > 0) { num3--; while (true) { switch (num3 % 2) { case 0: szptr[num2] = 0; num2++; mtfFreq[0]++; break; case 1: szptr[num2] = 1; num2++; mtfFreq[1]++; break; } if (num3 < 2) { break; } num3 = (num3 - 2) / 2; } num3 = 0; } szptr[num2] = (short)(num4 + 1); num2++; mtfFreq[num4 + 1]++; } if (num3 > 0) { num3--; while (true) { switch (num3 % 2) { case 0: szptr[num2] = 0; num2++; mtfFreq[0]++; break; case 1: szptr[num2] = 1; num2++; mtfFreq[1]++; break; } if (num3 < 2) { break; } num3 = (num3 - 2) / 2; } } szptr[num2] = (short)num; num2++; mtfFreq[num]++; nMTF = num2; } private static void Panic() { throw new BZip2Exception("BZip2 output stream panic"); } private static void HbMakeCodeLengths(char[] len, int[] freq, int alphaSize, int maxLen) { int[] array = new int[260]; int[] array2 = new int[516]; int[] array3 = new int[516]; for (int i = 0; i < alphaSize; i++) { array2[i + 1] = ((freq[i] == 0) ? 1 : freq[i]) << 8; } while (true) { int num = alphaSize; int num2 = 0; array[0] = 0; array2[0] = 0; array3[0] = -2; for (int j = 1; j <= alphaSize; j++) { array3[j] = -1; num2++; array[num2] = j; int num3 = num2; int num4 = array[num3]; while (array2[num4] < array2[array[num3 >> 1]]) { array[num3] = array[num3 >> 1]; num3 >>= 1; } array[num3] = num4; } if (num2 >= 260) { Panic(); } while (num2 > 1) { int num5 = array[1]; array[1] = array[num2]; num2--; int num6 = 1; int num7 = 0; int num8 = array[num6]; while (true) { num7 = num6 << 1; if (num7 > num2) { break; } if (num7 < num2 && array2[array[num7 + 1]] < array2[array[num7]]) { num7++; } if (array2[num8] < array2[array[num7]]) { break; } array[num6] = array[num7]; num6 = num7; } array[num6] = num8; int num9 = array[1]; array[1] = array[num2]; num2--; num6 = 1; num7 = 0; num8 = array[num6]; while (true) { num7 = num6 << 1; if (num7 > num2) { break; } if (num7 < num2 && array2[array[num7 + 1]] < array2[array[num7]]) { num7++; } if (array2[num8] < array2[array[num7]]) { break; } array[num6] = array[num7]; num6 = num7; } array[num6] = num8; num++; array3[num5] = (array3[num9] = num); array2[num] = (int)((array2[num5] & 0xFFFFFF00u) + (array2[num9] & 0xFFFFFF00u)) | (1 + (((array2[num5] & 0xFF) > (array2[num9] & 0xFF)) ? (array2[num5] & 0xFF) : (array2[num9] & 0xFF))); array3[num] = -1; num2++; array[num2] = num; num6 = num2; num8 = array[num6]; while (array2[num8] < array2[array[num6 >> 1]]) { array[num6] = array[num6 >> 1]; num6 >>= 1; } array[num6] = num8; } if (num >= 516) { Panic(); } bool flag = false; for (int k = 1; k <= alphaSize; k++) { int num10 = 0; int num11 = k; while (array3[num11] >= 0) { num11 = array3[num11]; num10++; } len[k - 1] = (char)num10; flag = flag || num10 > maxLen; } if (flag) { for (int l = 1; l < alphaSize; l++) { int num10 = array2[l] >> 8; num10 = 1 + num10 / 2; array2[l] = num10 << 8; } continue; } break; } } private static void HbAssignCodes(int[] code, char[] length, int minLen, int maxLen, int alphaSize) { int num = 0; for (int i = minLen; i <= maxLen; i++) { for (int j = 0; j < alphaSize; j++) { if (length[j] == i) { code[j] = num; num++; } } num <<= 1; } } private static byte Med3(byte a, byte b, byte c) { if (a > b) { byte num = a; a = b; b = num; } if (b > c) { byte num2 = b; b = c; c = num2; } if (a > b) { b = a; } return b; } } }