ICSharpCode.SharpZipLib.Zip.DynamicDiskDataSource - Coverage Report

# Summary

Class: | ICSharpCode.SharpZipLib.Zip.DynamicDiskDataSource
Assembly: | ICSharpCode.SharpZipLib
File(s): | C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Zip\ZipFile.cs
Covered lines: | 4
Uncovered lines: | 0
Coverable lines: | 4
Total lines: | 4263
Line coverage: | 100%
Branch coverage: | 100%

# Metrics

Method | Cyclomatic Complexity | Sequence Coverage | Branch Coverage
--- | --- | --- | ---
GetSource(...) | 2 | 100 | 100

# File(s)

## C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Zip\ZipFile.cs

| # | Line |  | Line coverage
--- | --- | --- | --- | ---
|  | `1` |  | `using System;`
|  | `2` |  | `using System.Collections;`
|  | `3` |  | `using System.IO;`
|  | `4` |  | `using System.Text;`
|  | `5` |  | `using System.Globalization;`
|  | `6` |  | `using System.Security.Cryptography;`
|  | `7` |  | `using ICSharpCode.SharpZipLib.Encryption;`
|  | `8` |  | `using ICSharpCode.SharpZipLib.Core;`
|  | `9` |  | `using ICSharpCode.SharpZipLib.Checksum;`
|  | `10` |  | `using ICSharpCode.SharpZipLib.Zip.Compression.Streams;`
|  | `11` |  | `using ICSharpCode.SharpZipLib.Zip.Compression;`
|  | `12` |  | ``
|  | `13` |  | `namespace ICSharpCode.SharpZipLib.Zip`
|  | `14` |  | `{`
|  | `15` |  | `#region Keys Required Event Args`
|  | `16` |  | `/// <summary>`
|  | `17` |  | `/// Arguments used with KeysRequiredEvent`
|  | `18` |  | `/// </summary>`
|  | `19` |  | `public class KeysRequiredEventArgs : EventArgs`
|  | `20` |  | `{`
|  | `21` |  | `#region Constructors`
|  | `22` |  | `/// <summary>`
|  | `23` |  | `/// Initialise a new instance of <see cref="KeysRequiredEventArgs"></see>`
|  | `24` |  | `/// </summary>`
|  | `25` |  | `/// <param name="name">The name of the file for which keys are required.</param>`
|  | `26` |  | `public KeysRequiredEventArgs(string name)`
|  | `27` |  | `{`
|  | `28` |  | `fileName = name;`
|  | `29` |  | `}`
|  | `30` |  | ``
|  | `31` |  | `/// <summary>`
|  | `32` |  | `/// Initialise a new instance of <see cref="KeysRequiredEventArgs"></see>`
|  | `33` |  | `/// </summary>`
|  | `34` |  | `/// <param name="name">The name of the file for which keys are required.</param>`
|  | `35` |  | `/// <param name="keyValue">The current key value.</param>`
|  | `36` |  | `public KeysRequiredEventArgs(string name, byte[] keyValue)`
|  | `37` |  | `{`
|  | `38` |  | `fileName = name;`
|  | `39` |  | `key = keyValue;`
|  | `40` |  | `}`
|  | `41` |  | ``
|  | `42` |  | `#endregion`
|  | `43` |  | `#region Properties`
|  | `44` |  | `/// <summary>`
|  | `45` |  | `/// Gets the name of the file for which keys are required.`
|  | `46` |  | `/// </summary>`
|  | `47` |  | `public string FileName {`
|  | `48` |  | `get { return fileName; }`
|  | `49` |  | `}`
|  | `50` |  | ``
|  | `51` |  | `/// <summary>`
|  | `52` |  | `/// Gets or sets the key value`
|  | `53` |  | `/// </summary>`
|  | `54` |  | `public byte[] Key {`
|  | `55` |  | `get { return key; }`
|  | `56` |  | `set { key = value; }`
|  | `57` |  | `}`
|  | `58` |  | `#endregion`
|  | `59` |  | ``
|  | `60` |  | `#region Instance Fields`
|  | `61` |  | `string fileName;`
|  | `62` |  | `byte[] key;`
|  | `63` |  | `#endregion`
|  | `64` |  | `}`
|  | `65` |  | `#endregion`
|  | `66` |  | ``
|  | `67` |  | `#region Test Definitions`
|  | `68` |  | `/// <summary>`
|  | `69` |  | `/// The strategy to apply to testing.`
|  | `70` |  | `/// </summary>`
|  | `71` |  | `public enum TestStrategy`
|  | `72` |  | `{`
|  | `73` |  | `/// <summary>`
|  | `74` |  | `/// Find the first error only.`
|  | `75` |  | `/// </summary>`
|  | `76` |  | `FindFirstError,`
|  | `77` |  | `/// <summary>`
|  | `78` |  | `/// Find all possible errors.`
|  | `79` |  | `/// </summary>`
|  | `80` |  | `FindAllErrors,`
|  | `81` |  | `}`
|  | `82` |  | ``
|  | `83` |  | `/// <summary>`
|  | `84` |  | `/// The operation in progress reported by a <see cref="ZipTestResultHandler"/> during testing.`
|  | `85` |  | `/// </summary>`
|  | `86` |  | `/// <seealso cref="ZipFile.TestArchive(bool)">TestArchive</seealso>`
|  | `87` |  | `public enum TestOperation`
|  | `88` |  | `{`
|  | `89` |  | `/// <summary>`
|  | `90` |  | `/// Setting up testing.`
|  | `91` |  | `/// </summary>`
|  | `92` |  | `Initialising,`
|  | `93` |  | ``
|  | `94` |  | `/// <summary>`
|  | `95` |  | `/// Testing an individual entries header`
|  | `96` |  | `/// </summary>`
|  | `97` |  | `EntryHeader,`
|  | `98` |  | ``
|  | `99` |  | `/// <summary>`
|  | `100` |  | `/// Testing an individual entries data`
|  | `101` |  | `/// </summary>`
|  | `102` |  | `EntryData,`
|  | `103` |  | ``
|  | `104` |  | `/// <summary>`
|  | `105` |  | `/// Testing an individual entry has completed.`
|  | `106` |  | `/// </summary>`
|  | `107` |  | `EntryComplete,`
|  | `108` |  | ``
|  | `109` |  | `/// <summary>`
|  | `110` |  | `/// Running miscellaneous tests`
|  | `111` |  | `/// </summary>`
|  | `112` |  | `MiscellaneousTests,`
|  | `113` |  | ``
|  | `114` |  | `/// <summary>`
|  | `115` |  | `/// Testing is complete`
|  | `116` |  | `/// </summary>`
|  | `117` |  | `Complete,`
|  | `118` |  | `}`
|  | `119` |  | ``
|  | `120` |  | `/// <summary>`
|  | `121` |  | `/// Status returned returned by <see cref="ZipTestResultHandler"/> during testing.`
|  | `122` |  | `/// </summary>`
|  | `123` |  | `/// <seealso cref="ZipFile.TestArchive(bool)">TestArchive</seealso>`
|  | `124` |  | `public class TestStatus`
|  | `125` |  | `{`
|  | `126` |  | `#region Constructors`
|  | `127` |  | `/// <summary>`
|  | `128` |  | `/// Initialise a new instance of <see cref="TestStatus"/>`
|  | `129` |  | `/// </summary>`
|  | `130` |  | `/// <param name="file">The <see cref="ZipFile"/> this status applies to.</param>`
|  | `131` |  | `public TestStatus(ZipFile file)`
|  | `132` |  | `{`
|  | `133` |  | `file_ = file;`
|  | `134` |  | `}`
|  | `135` |  | `#endregion`
|  | `136` |  | ``
|  | `137` |  | `#region Properties`
|  | `138` |  | ``
|  | `139` |  | `/// <summary>`
|  | `140` |  | `/// Get the current <see cref="TestOperation"/> in progress.`
|  | `141` |  | `/// </summary>`
|  | `142` |  | `public TestOperation Operation {`
|  | `143` |  | `get { return operation_; }`
|  | `144` |  | `}`
|  | `145` |  | ``
|  | `146` |  | `/// <summary>`
|  | `147` |  | `/// Get the <see cref="ZipFile"/> this status is applicable to.`
|  | `148` |  | `/// </summary>`
|  | `149` |  | `public ZipFile File {`
|  | `150` |  | `get { return file_; }`
|  | `151` |  | `}`
|  | `152` |  | ``
|  | `153` |  | `/// <summary>`
|  | `154` |  | `/// Get the current/last entry tested.`
|  | `155` |  | `/// </summary>`
|  | `156` |  | `public ZipEntry Entry {`
|  | `157` |  | `get { return entry_; }`
|  | `158` |  | `}`
|  | `159` |  | ``
|  | `160` |  | `/// <summary>`
|  | `161` |  | `/// Get the number of errors detected so far.`
|  | `162` |  | `/// </summary>`
|  | `163` |  | `public int ErrorCount {`
|  | `164` |  | `get { return errorCount_; }`
|  | `165` |  | `}`
|  | `166` |  | ``
|  | `167` |  | `/// <summary>`
|  | `168` |  | `/// Get the number of bytes tested so far for the current entry.`
|  | `169` |  | `/// </summary>`
|  | `170` |  | `public long BytesTested {`
|  | `171` |  | `get { return bytesTested_; }`
|  | `172` |  | `}`
|  | `173` |  | ``
|  | `174` |  | `/// <summary>`
|  | `175` |  | `/// Get a value indicating wether the last entry test was valid.`
|  | `176` |  | `/// </summary>`
|  | `177` |  | `public bool EntryValid {`
|  | `178` |  | `get { return entryValid_; }`
|  | `179` |  | `}`
|  | `180` |  | `#endregion`
|  | `181` |  | ``
|  | `182` |  | `#region Internal API`
|  | `183` |  | `internal void AddError()`
|  | `184` |  | `{`
|  | `185` |  | `errorCount_++;`
|  | `186` |  | `entryValid_ = false;`
|  | `187` |  | `}`
|  | `188` |  | ``
|  | `189` |  | `internal void SetOperation(TestOperation operation)`
|  | `190` |  | `{`
|  | `191` |  | `operation_ = operation;`
|  | `192` |  | `}`
|  | `193` |  | ``
|  | `194` |  | `internal void SetEntry(ZipEntry entry)`
|  | `195` |  | `{`
|  | `196` |  | `entry_ = entry;`
|  | `197` |  | `entryValid_ = true;`
|  | `198` |  | `bytesTested_ = 0;`
|  | `199` |  | `}`
|  | `200` |  | ``
|  | `201` |  | `internal void SetBytesTested(long value)`
|  | `202` |  | `{`
|  | `203` |  | `bytesTested_ = value;`
|  | `204` |  | `}`
|  | `205` |  | `#endregion`
|  | `206` |  | ``
|  | `207` |  | `#region Instance Fields`
|  | `208` |  | `ZipFile file_;`
|  | `209` |  | `ZipEntry entry_;`
|  | `210` |  | `bool entryValid_;`
|  | `211` |  | `int errorCount_;`
|  | `212` |  | `long bytesTested_;`
|  | `213` |  | `TestOperation operation_;`
|  | `214` |  | `#endregion`
|  | `215` |  | `}`
|  | `216` |  | ``
|  | `217` |  | `/// <summary>`
|  | `218` |  | `/// Delegate invoked during <see cref="ZipFile.TestArchive(bool, TestStrategy, ZipTestResultHandler)">testing</see> if`
|  | `219` |  | `/// </summary>`
|  | `220` |  | `/// <remarks>If the message is non-null an error has occured. If the message is null`
|  | `221` |  | `/// the operation as found in <see cref="TestStatus">status</see> has started.</remarks>`
|  | `222` |  | `public delegate void ZipTestResultHandler(TestStatus status, string message);`
|  | `223` |  | `#endregion`
|  | `224` |  | ``
|  | `225` |  | `#region Update Definitions`
|  | `226` |  | `/// <summary>`
|  | `227` |  | `/// The possible ways of <see cref="ZipFile.CommitUpdate()">applying updates</see> to an archive.`
|  | `228` |  | `/// </summary>`
|  | `229` |  | `public enum FileUpdateMode`
|  | `230` |  | `{`
|  | `231` |  | `/// <summary>`
|  | `232` |  | `/// Perform all updates on temporary files ensuring that the original file is saved.`
|  | `233` |  | `/// </summary>`
|  | `234` |  | `Safe,`
|  | `235` |  | `/// <summary>`
|  | `236` |  | `/// Update the archive directly, which is faster but less safe.`
|  | `237` |  | `/// </summary>`
|  | `238` |  | `Direct,`
|  | `239` |  | `}`
|  | `240` |  | `#endregion`
|  | `241` |  | ``
|  | `242` |  | `#region ZipFile Class`
|  | `243` |  | `/// <summary>`
|  | `244` |  | `/// This class represents a Zip archive. You can ask for the contained`
|  | `245` |  | `/// entries, or get an input stream for a file entry. The entry is`
|  | `246` |  | `/// automatically decompressed.`
|  | `247` |  | `///`
|  | `248` |  | `/// You can also update the archive adding or deleting entries.`
|  | `249` |  | `///`
|  | `250` |  | `/// This class is thread safe for input: You can open input streams for arbitrary`
|  | `251` |  | `/// entries in different threads.`
|  | `252` |  | `/// <br/>`
|  | `253` |  | `/// <br/>Author of the original java version : Jochen Hoenicke`
|  | `254` |  | `/// </summary>`
|  | `255` |  | `/// <example>`
|  | `256` |  | `/// <code>`
|  | `257` |  | `/// using System;`
|  | `258` |  | `/// using System.Text;`
|  | `259` |  | `/// using System.Collections;`
|  | `260` |  | `/// using System.IO;`
|  | `261` |  | `///`
|  | `262` |  | `/// using ICSharpCode.SharpZipLib.Zip;`
|  | `263` |  | `///`
|  | `264` |  | `/// class MainClass`
|  | `265` |  | `/// {`
|  | `266` |  | `/// static public void Main(string[] args)`
|  | `267` |  | `/// {`
|  | `268` |  | `/// using (ZipFile zFile = new ZipFile(args[0])) {`
|  | `269` |  | `/// Console.WriteLine("Listing of : " + zFile.Name);`
|  | `270` |  | `/// Console.WriteLine("");`
|  | `271` |  | `/// Console.WriteLine("Raw Size Size Date Time Name");`
|  | `272` |  | `/// Console.WriteLine("-------- -------- -------- ------ ---------");`
|  | `273` |  | `/// foreach (ZipEntry e in zFile) {`
|  | `274` |  | `/// if ( e.IsFile ) {`
|  | `275` |  | `/// DateTime d = e.DateTime;`
|  | `276` |  | `/// Console.WriteLine("{0, -10}{1, -10}{2} {3} {4}", e.Size, e.CompressedSize,`
|  | `277` |  | `/// d.ToString("dd-MM-yy"), d.ToString("HH:mm"),`
|  | `278` |  | `/// e.Name);`
|  | `279` |  | `/// }`
|  | `280` |  | `/// }`
|  | `281` |  | `/// }`
|  | `282` |  | `/// }`
|  | `283` |  | `/// }`
|  | `284` |  | `/// </code>`
|  | `285` |  | `/// </example>`
|  | `286` |  | `public class ZipFile : IEnumerable, IDisposable`
|  | `287` |  | `{`
|  | `288` |  | `#region KeyHandling`
|  | `289` |  | ``
|  | `290` |  | `/// <summary>`
|  | `291` |  | `/// Delegate for handling keys/password setting during compresion/decompression.`
|  | `292` |  | `/// </summary>`
|  | `293` |  | `public delegate void KeysRequiredEventHandler(`
|  | `294` |  | `object sender,`
|  | `295` |  | `KeysRequiredEventArgs e`
|  | `296` |  | `);`
|  | `297` |  | ``
|  | `298` |  | `/// <summary>`
|  | `299` |  | `/// Event handler for handling encryption keys.`
|  | `300` |  | `/// </summary>`
|  | `301` |  | `public KeysRequiredEventHandler KeysRequired;`
|  | `302` |  | ``
|  | `303` |  | `/// <summary>`
|  | `304` |  | `/// Handles getting of encryption keys when required.`
|  | `305` |  | `/// </summary>`
|  | `306` |  | `/// <param name="fileName">The file for which encryption keys are required.</param>`
|  | `307` |  | `void OnKeysRequired(string fileName)`
|  | `308` |  | `{`
|  | `309` |  | `if (KeysRequired != null) {`
|  | `310` |  | `var krea = new KeysRequiredEventArgs(fileName, key);`
|  | `311` |  | `KeysRequired(this, krea);`
|  | `312` |  | `key = krea.Key;`
|  | `313` |  | `}`
|  | `314` |  | `}`
|  | `315` |  | ``
|  | `316` |  | `/// <summary>`
|  | `317` |  | `/// Get/set the encryption key value.`
|  | `318` |  | `/// </summary>`
|  | `319` |  | `byte[] Key {`
|  | `320` |  | `get { return key; }`
|  | `321` |  | `set { key = value; }`
|  | `322` |  | `}`
|  | `323` |  | ``
|  | `324` |  | `/// <summary>`
|  | `325` |  | `/// Password to be used for encrypting/decrypting files.`
|  | `326` |  | `/// </summary>`
|  | `327` |  | `/// <remarks>Set to null if no password is required.</remarks>`
|  | `328` |  | `public string Password {`
|  | `329` |  | `set {`
|  | `330` |  | `if (string.IsNullOrEmpty(value)) {`
|  | `331` |  | `key = null;`
|  | `332` |  | `} else {`
|  | `333` |  | `rawPassword_ = value;`
|  | `334` |  | `key = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(value));`
|  | `335` |  | `}`
|  | `336` |  | `}`
|  | `337` |  | `}`
|  | `338` |  | ``
|  | `339` |  | `/// <summary>`
|  | `340` |  | `/// Get a value indicating wether encryption keys are currently available.`
|  | `341` |  | `/// </summary>`
|  | `342` |  | `bool HaveKeys {`
|  | `343` |  | `get { return key != null; }`
|  | `344` |  | `}`
|  | `345` |  | `#endregion`
|  | `346` |  | ``
|  | `347` |  | `#region Constructors`
|  | `348` |  | `/// <summary>`
|  | `349` |  | `/// Opens a Zip file with the given name for reading.`
|  | `350` |  | `/// </summary>`
|  | `351` |  | `/// <param name="name">The name of the file to open.</param>`
|  | `352` |  | `/// <exception cref="ArgumentNullException">The argument supplied is null.</exception>`
|  | `353` |  | `/// <exception cref="IOException">`
|  | `354` |  | `/// An i/o error occurs`
|  | `355` |  | `/// </exception>`
|  | `356` |  | `/// <exception cref="ZipException">`
|  | `357` |  | `/// The file doesn't contain a valid zip archive.`
|  | `358` |  | `/// </exception>`
|  | `359` |  | `public ZipFile(string name)`
|  | `360` |  | `{`
|  | `361` |  | `if (name == null) {`
|  | `362` |  | `throw new ArgumentNullException(nameof(name));`
|  | `363` |  | `}`
|  | `364` |  | ``
|  | `365` |  | `name_ = name;`
|  | `366` |  | ``
|  | `367` |  | `baseStream_ = File.Open(name, FileMode.Open, FileAccess.Read, FileShare.Read);`
|  | `368` |  | `isStreamOwner = true;`
|  | `369` |  | ``
|  | `370` |  | `try {`
|  | `371` |  | `ReadEntries();`
|  | `372` |  | `} catch {`
|  | `373` |  | `DisposeInternal(true);`
|  | `374` |  | `throw;`
|  | `375` |  | `}`
|  | `376` |  | `}`
|  | `377` |  | ``
|  | `378` |  | `/// <summary>`
|  | `379` |  | `/// Opens a Zip file reading the given <see cref="FileStream"/>.`
|  | `380` |  | `/// </summary>`
|  | `381` |  | `/// <param name="file">The <see cref="FileStream"/> to read archive data from.</param>`
|  | `382` |  | `/// <exception cref="ArgumentNullException">The supplied argument is null.</exception>`
|  | `383` |  | `/// <exception cref="IOException">`
|  | `384` |  | `/// An i/o error occurs.`
|  | `385` |  | `/// </exception>`
|  | `386` |  | `/// <exception cref="ZipException">`
|  | `387` |  | `/// The file doesn't contain a valid zip archive.`
|  | `388` |  | `/// </exception>`
|  | `389` |  | `public ZipFile(FileStream file)`
|  | `390` |  | `{`
|  | `391` |  | `if (file == null) {`
|  | `392` |  | `throw new ArgumentNullException(nameof(file));`
|  | `393` |  | `}`
|  | `394` |  | ``
|  | `395` |  | `if (!file.CanSeek) {`
|  | `396` |  | `throw new ArgumentException("Stream is not seekable", nameof(file));`
|  | `397` |  | `}`
|  | `398` |  | ``
|  | `399` |  | `baseStream_ = file;`
|  | `400` |  | `name_ = file.Name;`
|  | `401` |  | `isStreamOwner = true;`
|  | `402` |  | ``
|  | `403` |  | `try {`
|  | `404` |  | `ReadEntries();`
|  | `405` |  | `} catch {`
|  | `406` |  | `DisposeInternal(true);`
|  | `407` |  | `throw;`
|  | `408` |  | `}`
|  | `409` |  | `}`
|  | `410` |  | ``
|  | `411` |  | `/// <summary>`
|  | `412` |  | `/// Opens a Zip file reading the given <see cref="Stream"/>.`
|  | `413` |  | `/// </summary>`
|  | `414` |  | `/// <param name="stream">The <see cref="Stream"/> to read archive data from.</param>`
|  | `415` |  | `/// <exception cref="IOException">`
|  | `416` |  | `/// An i/o error occurs`
|  | `417` |  | `/// </exception>`
|  | `418` |  | `/// <exception cref="ZipException">`
|  | `419` |  | `/// The stream doesn't contain a valid zip archive.<br/>`
|  | `420` |  | `/// </exception>`
|  | `421` |  | `/// <exception cref="ArgumentException">`
|  | `422` |  | `/// The <see cref="Stream">stream</see> doesnt support seeking.`
|  | `423` |  | `/// </exception>`
|  | `424` |  | `/// <exception cref="ArgumentNullException">`
|  | `425` |  | `/// The <see cref="Stream">stream</see> argument is null.`
|  | `426` |  | `/// </exception>`
|  | `427` |  | `public ZipFile(Stream stream)`
|  | `428` |  | `{`
|  | `429` |  | `if (stream == null) {`
|  | `430` |  | `throw new ArgumentNullException(nameof(stream));`
|  | `431` |  | `}`
|  | `432` |  | ``
|  | `433` |  | `if (!stream.CanSeek) {`
|  | `434` |  | `throw new ArgumentException("Stream is not seekable", nameof(stream));`
|  | `435` |  | `}`
|  | `436` |  | ``
|  | `437` |  | `baseStream_ = stream;`
|  | `438` |  | `isStreamOwner = true;`
|  | `439` |  | ``
|  | `440` |  | `if (baseStream_.Length > 0) {`
|  | `441` |  | `try {`
|  | `442` |  | `ReadEntries();`
|  | `443` |  | `} catch {`
|  | `444` |  | `DisposeInternal(true);`
|  | `445` |  | `throw;`
|  | `446` |  | `}`
|  | `447` |  | `} else {`
|  | `448` |  | `entries_ = new ZipEntry[0];`
|  | `449` |  | `isNewArchive_ = true;`
|  | `450` |  | `}`
|  | `451` |  | `}`
|  | `452` |  | ``
|  | `453` |  | `/// <summary>`
|  | `454` |  | `/// Initialises a default <see cref="ZipFile"/> instance with no entries and no file storage.`
|  | `455` |  | `/// </summary>`
|  | `456` |  | `internal ZipFile()`
|  | `457` |  | `{`
|  | `458` |  | `entries_ = new ZipEntry[0];`
|  | `459` |  | `isNewArchive_ = true;`
|  | `460` |  | `}`
|  | `461` |  | ``
|  | `462` |  | `#endregion`
|  | `463` |  | ``
|  | `464` |  | `#region Destructors and Closing`
|  | `465` |  | `/// <summary>`
|  | `466` |  | `/// Finalize this instance.`
|  | `467` |  | `/// </summary>`
|  | `468` |  | `~ZipFile()`
|  | `469` |  | `{`
|  | `470` |  | `Dispose(false);`
|  | `471` |  | `}`
|  | `472` |  | ``
|  | `473` |  | `/// <summary>`
|  | `474` |  | `/// Closes the ZipFile. If the stream is <see cref="IsStreamOwner">owned</see> then this also closes the underlying`
|  | `475` |  | `/// Once closed, no further instance methods should be called.`
|  | `476` |  | `/// </summary>`
|  | `477` |  | `/// <exception cref="System.IO.IOException">`
|  | `478` |  | `/// An i/o error occurs.`
|  | `479` |  | `/// </exception>`
|  | `480` |  | `public void Close()`
|  | `481` |  | `{`
|  | `482` |  | `DisposeInternal(true);`
|  | `483` |  | `GC.SuppressFinalize(this);`
|  | `484` |  | `}`
|  | `485` |  | ``
|  | `486` |  | `#endregion`
|  | `487` |  | ``
|  | `488` |  | `#region Creators`
|  | `489` |  | `/// <summary>`
|  | `490` |  | `/// Create a new <see cref="ZipFile"/> whose data will be stored in a file.`
|  | `491` |  | `/// </summary>`
|  | `492` |  | `/// <param name="fileName">The name of the archive to create.</param>`
|  | `493` |  | `/// <returns>Returns the newly created <see cref="ZipFile"/></returns>`
|  | `494` |  | `/// <exception cref="ArgumentNullException"><paramref name="fileName"></paramref> is null</exception>`
|  | `495` |  | `public static ZipFile Create(string fileName)`
|  | `496` |  | `{`
|  | `497` |  | `if (fileName == null) {`
|  | `498` |  | `throw new ArgumentNullException(nameof(fileName));`
|  | `499` |  | `}`
|  | `500` |  | ``
|  | `501` |  | `FileStream fs = File.Create(fileName);`
|  | `502` |  | ``
|  | `503` |  | `var result = new ZipFile();`
|  | `504` |  | `result.name_ = fileName;`
|  | `505` |  | `result.baseStream_ = fs;`
|  | `506` |  | `result.isStreamOwner = true;`
|  | `507` |  | `return result;`
|  | `508` |  | `}`
|  | `509` |  | ``
|  | `510` |  | `/// <summary>`
|  | `511` |  | `/// Create a new <see cref="ZipFile"/> whose data will be stored on a stream.`
|  | `512` |  | `/// </summary>`
|  | `513` |  | `/// <param name="outStream">The stream providing data storage.</param>`
|  | `514` |  | `/// <returns>Returns the newly created <see cref="ZipFile"/></returns>`
|  | `515` |  | `/// <exception cref="ArgumentNullException"><paramref name="outStream"> is null</paramref></exception>`
|  | `516` |  | `/// <exception cref="ArgumentException"><paramref name="outStream"> doesnt support writing.</paramref></exception>`
|  | `517` |  | `public static ZipFile Create(Stream outStream)`
|  | `518` |  | `{`
|  | `519` |  | `if (outStream == null) {`
|  | `520` |  | `throw new ArgumentNullException(nameof(outStream));`
|  | `521` |  | `}`
|  | `522` |  | ``
|  | `523` |  | `if (!outStream.CanWrite) {`
|  | `524` |  | `throw new ArgumentException("Stream is not writeable", nameof(outStream));`
|  | `525` |  | `}`
|  | `526` |  | ``
|  | `527` |  | `if (!outStream.CanSeek) {`
|  | `528` |  | `throw new ArgumentException("Stream is not seekable", nameof(outStream));`
|  | `529` |  | `}`
|  | `530` |  | ``
|  | `531` |  | `var result = new ZipFile();`
|  | `532` |  | `result.baseStream_ = outStream;`
|  | `533` |  | `return result;`
|  | `534` |  | `}`
|  | `535` |  | ``
|  | `536` |  | `#endregion`
|  | `537` |  | ``
|  | `538` |  | `#region Properties`
|  | `539` |  | `/// <summary>`
|  | `540` |  | `/// Get/set a flag indicating if the underlying stream is owned by the ZipFile instance.`
|  | `541` |  | `/// If the flag is true then the stream will be closed when <see cref="Close">Close</see> is called.`
|  | `542` |  | `/// </summary>`
|  | `543` |  | `/// <remarks>`
|  | `544` |  | `/// The default value is true in all cases.`
|  | `545` |  | `/// </remarks>`
|  | `546` |  | `public bool IsStreamOwner {`
|  | `547` |  | `get { return isStreamOwner; }`
|  | `548` |  | `set { isStreamOwner = value; }`
|  | `549` |  | `}`
|  | `550` |  | ``
|  | `551` |  | `/// <summary>`
|  | `552` |  | `/// Get a value indicating wether`
|  | `553` |  | `/// this archive is embedded in another file or not.`
|  | `554` |  | `/// </summary>`
|  | `555` |  | `public bool IsEmbeddedArchive {`
|  | `556` |  | `// Not strictly correct in all circumstances currently`
|  | `557` |  | `get { return offsetOfFirstEntry > 0; }`
|  | `558` |  | `}`
|  | `559` |  | ``
|  | `560` |  | `/// <summary>`
|  | `561` |  | `/// Get a value indicating that this archive is a new one.`
|  | `562` |  | `/// </summary>`
|  | `563` |  | `public bool IsNewArchive {`
|  | `564` |  | `get { return isNewArchive_; }`
|  | `565` |  | `}`
|  | `566` |  | ``
|  | `567` |  | `/// <summary>`
|  | `568` |  | `/// Gets the comment for the zip file.`
|  | `569` |  | `/// </summary>`
|  | `570` |  | `public string ZipFileComment {`
|  | `571` |  | `get { return comment_; }`
|  | `572` |  | `}`
|  | `573` |  | ``
|  | `574` |  | `/// <summary>`
|  | `575` |  | `/// Gets the name of this zip file.`
|  | `576` |  | `/// </summary>`
|  | `577` |  | `public string Name {`
|  | `578` |  | `get { return name_; }`
|  | `579` |  | `}`
|  | `580` |  | ``
|  | `581` |  | `/// <summary>`
|  | `582` |  | `/// Gets the number of entries in this zip file.`
|  | `583` |  | `/// </summary>`
|  | `584` |  | `/// <exception cref="InvalidOperationException">`
|  | `585` |  | `/// The Zip file has been closed.`
|  | `586` |  | `/// </exception>`
|  | `587` |  | `[Obsolete("Use the Count property instead")]`
|  | `588` |  | `public int Size {`
|  | `589` |  | `get {`
|  | `590` |  | `return entries_.Length;`
|  | `591` |  | `}`
|  | `592` |  | `}`
|  | `593` |  | ``
|  | `594` |  | `/// <summary>`
|  | `595` |  | `/// Get the number of entries contained in this <see cref="ZipFile"/>.`
|  | `596` |  | `/// </summary>`
|  | `597` |  | `public long Count {`
|  | `598` |  | `get {`
|  | `599` |  | `return entries_.Length;`
|  | `600` |  | `}`
|  | `601` |  | `}`
|  | `602` |  | ``
|  | `603` |  | `/// <summary>`
|  | `604` |  | `/// Indexer property for ZipEntries`
|  | `605` |  | `/// </summary>`
|  | `606` |  | `[System.Runtime.CompilerServices.IndexerNameAttribute("EntryByIndex")]`
|  | `607` |  | `public ZipEntry this[int index] {`
|  | `608` |  | `get {`
|  | `609` |  | `return (ZipEntry)entries_[index].Clone();`
|  | `610` |  | `}`
|  | `611` |  | `}`
|  | `612` |  | ``
|  | `613` |  | `#endregion`
|  | `614` |  | ``
|  | `615` |  | `#region Input Handling`
|  | `616` |  | `/// <summary>`
|  | `617` |  | `/// Gets an enumerator for the Zip entries in this Zip file.`
|  | `618` |  | `/// </summary>`
|  | `619` |  | `/// <returns>Returns an <see cref="IEnumerator"/> for this archive.</returns>`
|  | `620` |  | `/// <exception cref="ObjectDisposedException">`
|  | `621` |  | `/// The Zip file has been closed.`
|  | `622` |  | `/// </exception>`
|  | `623` |  | `public IEnumerator GetEnumerator()`
|  | `624` |  | `{`
|  | `625` |  | `if (isDisposed_) {`
|  | `626` |  | `throw new ObjectDisposedException("ZipFile");`
|  | `627` |  | `}`
|  | `628` |  | ``
|  | `629` |  | `return new ZipEntryEnumerator(entries_);`
|  | `630` |  | `}`
|  | `631` |  | ``
|  | `632` |  | `/// <summary>`
|  | `633` |  | `/// Return the index of the entry with a matching name`
|  | `634` |  | `/// </summary>`
|  | `635` |  | `/// <param name="name">Entry name to find</param>`
|  | `636` |  | `/// <param name="ignoreCase">If true the comparison is case insensitive</param>`
|  | `637` |  | `/// <returns>The index position of the matching entry or -1 if not found</returns>`
|  | `638` |  | `/// <exception cref="ObjectDisposedException">`
|  | `639` |  | `/// The Zip file has been closed.`
|  | `640` |  | `/// </exception>`
|  | `641` |  | `public int FindEntry(string name, bool ignoreCase)`
|  | `642` |  | `{`
|  | `643` |  | `if (isDisposed_) {`
|  | `644` |  | `throw new ObjectDisposedException("ZipFile");`
|  | `645` |  | `}`
|  | `646` |  | ``
|  | `647` |  | `// TODO: This will be slow as the next ice age for huge archives!`
|  | `648` |  | `for (int i = 0; i < entries_.Length; i++) {`
|  | `649` |  | `if (string.Compare(name, entries_[i].Name, ignoreCase, CultureInfo.InvariantCulture) == 0) {`
|  | `650` |  | `return i;`
|  | `651` |  | `}`
|  | `652` |  | `}`
|  | `653` |  | `return -1;`
|  | `654` |  | `}`
|  | `655` |  | ``
|  | `656` |  | `/// <summary>`
|  | `657` |  | `/// Searches for a zip entry in this archive with the given name.`
|  | `658` |  | `/// String comparisons are case insensitive`
|  | `659` |  | `/// </summary>`
|  | `660` |  | `/// <param name="name">`
|  | `661` |  | `/// The name to find. May contain directory components separated by slashes ('/').`
|  | `662` |  | `/// </param>`
|  | `663` |  | `/// <returns>`
|  | `664` |  | `/// A clone of the zip entry, or null if no entry with that name exists.`
|  | `665` |  | `/// </returns>`
|  | `666` |  | `/// <exception cref="ObjectDisposedException">`
|  | `667` |  | `/// The Zip file has been closed.`
|  | `668` |  | `/// </exception>`
|  | `669` |  | `public ZipEntry GetEntry(string name)`
|  | `670` |  | `{`
|  | `671` |  | `if (isDisposed_) {`
|  | `672` |  | `throw new ObjectDisposedException("ZipFile");`
|  | `673` |  | `}`
|  | `674` |  | ``
|  | `675` |  | `int index = FindEntry(name, true);`
|  | `676` |  | `return (index >= 0) ? (ZipEntry)entries_[index].Clone() : null;`
|  | `677` |  | `}`
|  | `678` |  | ``
|  | `679` |  | `/// <summary>`
|  | `680` |  | `/// Gets an input stream for reading the given zip entry data in an uncompressed form.`
|  | `681` |  | `/// Normally the <see cref="ZipEntry"/> should be an entry returned by GetEntry().`
|  | `682` |  | `/// </summary>`
|  | `683` |  | `/// <param name="entry">The <see cref="ZipEntry"/> to obtain a data <see cref="Stream"/> for</param>`
|  | `684` |  | `/// <returns>An input <see cref="Stream"/> containing data for this <see cref="ZipEntry"/></returns>`
|  | `685` |  | `/// <exception cref="ObjectDisposedException">`
|  | `686` |  | `/// The ZipFile has already been closed`
|  | `687` |  | `/// </exception>`
|  | `688` |  | `/// <exception cref="ICSharpCode.SharpZipLib.Zip.ZipException">`
|  | `689` |  | `/// The compression method for the entry is unknown`
|  | `690` |  | `/// </exception>`
|  | `691` |  | `/// <exception cref="IndexOutOfRangeException">`
|  | `692` |  | `/// The entry is not found in the ZipFile`
|  | `693` |  | `/// </exception>`
|  | `694` |  | `public Stream GetInputStream(ZipEntry entry)`
|  | `695` |  | `{`
|  | `696` |  | `if (entry == null) {`
|  | `697` |  | `throw new ArgumentNullException(nameof(entry));`
|  | `698` |  | `}`
|  | `699` |  | ``
|  | `700` |  | `if (isDisposed_) {`
|  | `701` |  | `throw new ObjectDisposedException("ZipFile");`
|  | `702` |  | `}`
|  | `703` |  | ``
|  | `704` |  | `long index = entry.ZipFileIndex;`
|  | `705` |  | `if ((index < 0) || (index >= entries_.Length) || (entries_[index].Name != entry.Name)) {`
|  | `706` |  | `index = FindEntry(entry.Name, true);`
|  | `707` |  | `if (index < 0) {`
|  | `708` |  | `throw new ZipException("Entry cannot be found");`
|  | `709` |  | `}`
|  | `710` |  | `}`
|  | `711` |  | `return GetInputStream(index);`
|  | `712` |  | `}`
|  | `713` |  | ``
|  | `714` |  | `/// <summary>`
|  | `715` |  | `/// Creates an input stream reading a zip entry`
|  | `716` |  | `/// </summary>`
|  | `717` |  | `/// <param name="entryIndex">The index of the entry to obtain an input stream for.</param>`
|  | `718` |  | `/// <returns>`
|  | `719` |  | `/// An input <see cref="Stream"/> containing data for this <paramref name="entryIndex"/>`
|  | `720` |  | `/// </returns>`
|  | `721` |  | `/// <exception cref="ObjectDisposedException">`
|  | `722` |  | `/// The ZipFile has already been closed`
|  | `723` |  | `/// </exception>`
|  | `724` |  | `/// <exception cref="ICSharpCode.SharpZipLib.Zip.ZipException">`
|  | `725` |  | `/// The compression method for the entry is unknown`
|  | `726` |  | `/// </exception>`
|  | `727` |  | `/// <exception cref="IndexOutOfRangeException">`
|  | `728` |  | `/// The entry is not found in the ZipFile`
|  | `729` |  | `/// </exception>`
|  | `730` |  | `public Stream GetInputStream(long entryIndex)`
|  | `731` |  | `{`
|  | `732` |  | `if (isDisposed_) {`
|  | `733` |  | `throw new ObjectDisposedException("ZipFile");`
|  | `734` |  | `}`
|  | `735` |  | ``
|  | `736` |  | `long start = LocateEntry(entries_[entryIndex]);`
|  | `737` |  | `CompressionMethod method = entries_[entryIndex].CompressionMethod;`
|  | `738` |  | `Stream result = new PartialInputStream(this, start, entries_[entryIndex].CompressedSize);`
|  | `739` |  | ``
|  | `740` |  | `if (entries_[entryIndex].IsCrypted == true) {`
|  | `741` |  | `result = CreateAndInitDecryptionStream(result, entries_[entryIndex]);`
|  | `742` |  | `if (result == null) {`
|  | `743` |  | `throw new ZipException("Unable to decrypt this entry");`
|  | `744` |  | `}`
|  | `745` |  | `}`
|  | `746` |  | ``
|  | `747` |  | `switch (method) {`
|  | `748` |  | `case CompressionMethod.Stored:`
|  | `749` |  | `// read as is.`
|  | `750` |  | `break;`
|  | `751` |  | ``
|  | `752` |  | `case CompressionMethod.Deflated:`
|  | `753` |  | `// No need to worry about ownership and closing as underlying stream close does nothing.`
|  | `754` |  | `result = new InflaterInputStream(result, new Inflater(true));`
|  | `755` |  | `break;`
|  | `756` |  | ``
|  | `757` |  | `default:`
|  | `758` |  | `throw new ZipException("Unsupported compression method " + method);`
|  | `759` |  | `}`
|  | `760` |  | ``
|  | `761` |  | `return result;`
|  | `762` |  | `}`
|  | `763` |  | ``
|  | `764` |  | `#endregion`
|  | `765` |  | ``
|  | `766` |  | `#region Archive Testing`
|  | `767` |  | `/// <summary>`
|  | `768` |  | `/// Test an archive for integrity/validity`
|  | `769` |  | `/// </summary>`
|  | `770` |  | `/// <param name="testData">Perform low level data Crc check</param>`
|  | `771` |  | `/// <returns>true if all tests pass, false otherwise</returns>`
|  | `772` |  | `/// <remarks>Testing will terminate on the first error found.</remarks>`
|  | `773` |  | `public bool TestArchive(bool testData)`
|  | `774` |  | `{`
|  | `775` |  | `return TestArchive(testData, TestStrategy.FindFirstError, null);`
|  | `776` |  | `}`
|  | `777` |  | ``
|  | `778` |  | `/// <summary>`
|  | `779` |  | `/// Test an archive for integrity/validity`
|  | `780` |  | `/// </summary>`
|  | `781` |  | `/// <param name="testData">Perform low level data Crc check</param>`
|  | `782` |  | `/// <param name="strategy">The <see cref="TestStrategy"></see> to apply.</param>`
|  | `783` |  | `/// <param name="resultHandler">The <see cref="ZipTestResultHandler"></see> handler to call during testing.</param>`
|  | `784` |  | `/// <returns>true if all tests pass, false otherwise</returns>`
|  | `785` |  | `/// <exception cref="ObjectDisposedException">The object has already been closed.</exception>`
|  | `786` |  | `public bool TestArchive(bool testData, TestStrategy strategy, ZipTestResultHandler resultHandler)`
|  | `787` |  | `{`
|  | `788` |  | `if (isDisposed_) {`
|  | `789` |  | `throw new ObjectDisposedException("ZipFile");`
|  | `790` |  | `}`
|  | `791` |  | ``
|  | `792` |  | `var status = new TestStatus(this);`
|  | `793` |  | ``
|  | `794` |  | `if (resultHandler != null) {`
|  | `795` |  | `resultHandler(status, null);`
|  | `796` |  | `}`
|  | `797` |  | ``
|  | `798` |  | `HeaderTest test = testData ? (HeaderTest.Header | HeaderTest.Extract) : HeaderTest.Header;`
|  | `799` |  | ``
|  | `800` |  | `bool testing = true;`
|  | `801` |  | ``
|  | `802` |  | `try {`
|  | `803` |  | `int entryIndex = 0;`
|  | `804` |  | ``
|  | `805` |  | `while (testing && (entryIndex < Count)) {`
|  | `806` |  | `if (resultHandler != null) {`
|  | `807` |  | `status.SetEntry(this[entryIndex]);`
|  | `808` |  | `status.SetOperation(TestOperation.EntryHeader);`
|  | `809` |  | `resultHandler(status, null);`
|  | `810` |  | `}`
|  | `811` |  | ``
|  | `812` |  | `try {`
|  | `813` |  | `TestLocalHeader(this[entryIndex], test);`
|  | `814` |  | `} catch (ZipException ex) {`
|  | `815` |  | `status.AddError();`
|  | `816` |  | ``
|  | `817` |  | `if (resultHandler != null) {`
|  | `818` |  | `resultHandler(status,`
|  | `819` |  | `string.Format("Exception during test - '{0}'", ex.Message));`
|  | `820` |  | `}`
|  | `821` |  | ``
|  | `822` |  | `testing &= strategy != TestStrategy.FindFirstError;`
|  | `823` |  | `}`
|  | `824` |  | ``
|  | `825` |  | `if (testing && testData && this[entryIndex].IsFile) {`
|  | `826` |  | `if (resultHandler != null) {`
|  | `827` |  | `status.SetOperation(TestOperation.EntryData);`
|  | `828` |  | `resultHandler(status, null);`
|  | `829` |  | `}`
|  | `830` |  | ``
|  | `831` |  | `var crc = new Crc32();`
|  | `832` |  | ``
|  | `833` |  | `using (Stream entryStream = this.GetInputStream(this[entryIndex])) {`
|  | `834` |  | ``
|  | `835` |  | `byte[] buffer = new byte[4096];`
|  | `836` |  | `long totalBytes = 0;`
|  | `837` |  | `int bytesRead;`
|  | `838` |  | `while ((bytesRead = entryStream.Read(buffer, 0, buffer.Length)) > 0) {`
|  | `839` |  | `crc.Update(buffer, 0, bytesRead);`
|  | `840` |  | ``
|  | `841` |  | `if (resultHandler != null) {`
|  | `842` |  | `totalBytes += bytesRead;`
|  | `843` |  | `status.SetBytesTested(totalBytes);`
|  | `844` |  | `resultHandler(status, null);`
|  | `845` |  | `}`
|  | `846` |  | `}`
|  | `847` |  | `}`
|  | `848` |  | ``
|  | `849` |  | `if (this[entryIndex].Crc != crc.Value) {`
|  | `850` |  | `status.AddError();`
|  | `851` |  | ``
|  | `852` |  | `if (resultHandler != null) {`
|  | `853` |  | `resultHandler(status, "CRC mismatch");`
|  | `854` |  | `}`
|  | `855` |  | ``
|  | `856` |  | `testing &= strategy != TestStrategy.FindFirstError;`
|  | `857` |  | `}`
|  | `858` |  | ``
|  | `859` |  | `if ((this[entryIndex].Flags & (int)GeneralBitFlags.Descriptor) != 0) {`
|  | `860` |  | `var helper = new ZipHelperStream(baseStream_);`
|  | `861` |  | `var data = new DescriptorData();`
|  | `862` |  | `helper.ReadDataDescriptor(this[entryIndex].LocalHeaderRequiresZip64, data);`
|  | `863` |  | `if (this[entryIndex].Crc != data.Crc) {`
|  | `864` |  | `status.AddError();`
|  | `865` |  | `}`
|  | `866` |  | ``
|  | `867` |  | `if (this[entryIndex].CompressedSize != data.CompressedSize) {`
|  | `868` |  | `status.AddError();`
|  | `869` |  | `}`
|  | `870` |  | ``
|  | `871` |  | `if (this[entryIndex].Size != data.Size) {`
|  | `872` |  | `status.AddError();`
|  | `873` |  | `}`
|  | `874` |  | `}`
|  | `875` |  | `}`
|  | `876` |  | ``
|  | `877` |  | `if (resultHandler != null) {`
|  | `878` |  | `status.SetOperation(TestOperation.EntryComplete);`
|  | `879` |  | `resultHandler(status, null);`
|  | `880` |  | `}`
|  | `881` |  | ``
|  | `882` |  | `entryIndex += 1;`
|  | `883` |  | `}`
|  | `884` |  | ``
|  | `885` |  | `if (resultHandler != null) {`
|  | `886` |  | `status.SetOperation(TestOperation.MiscellaneousTests);`
|  | `887` |  | `resultHandler(status, null);`
|  | `888` |  | `}`
|  | `889` |  | ``
|  | `890` |  | `// TODO: the 'Corrina Johns' test where local headers are missing from`
|  | `891` |  | `// the central directory. They are therefore invisible to many archivers.`
|  | `892` |  | `} catch (Exception ex) {`
|  | `893` |  | `status.AddError();`
|  | `894` |  | ``
|  | `895` |  | `if (resultHandler != null) {`
|  | `896` |  | `resultHandler(status, string.Format("Exception during test - '{0}'", ex.Message));`
|  | `897` |  | `}`
|  | `898` |  | `}`
|  | `899` |  | ``
|  | `900` |  | `if (resultHandler != null) {`
|  | `901` |  | `status.SetOperation(TestOperation.Complete);`
|  | `902` |  | `status.SetEntry(null);`
|  | `903` |  | `resultHandler(status, null);`
|  | `904` |  | `}`
|  | `905` |  | ``
|  | `906` |  | `return (status.ErrorCount == 0);`
|  | `907` |  | `}`
|  | `908` |  | ``
|  | `909` |  | `[Flags]`
|  | `910` |  | `enum HeaderTest`
|  | `911` |  | `{`
|  | `912` |  | `Extract = 0x01, // Check that this header represents an entry whose data can be extracted`
|  | `913` |  | `Header = 0x02, // Check that this header contents are valid`
|  | `914` |  | `}`
|  | `915` |  | ``
|  | `916` |  | `/// <summary>`
|  | `917` |  | `/// Test a local header against that provided from the central directory`
|  | `918` |  | `/// </summary>`
|  | `919` |  | `/// <param name="entry">`
|  | `920` |  | `/// The entry to test against`
|  | `921` |  | `/// </param>`
|  | `922` |  | `/// <param name="tests">The type of <see cref="HeaderTest">tests</see> to carry out.</param>`
|  | `923` |  | `/// <returns>The offset of the entries data in the file</returns>`
|  | `924` |  | `long TestLocalHeader(ZipEntry entry, HeaderTest tests)`
|  | `925` |  | `{`
|  | `926` |  | `lock (baseStream_) {`
|  | `927` |  | `bool testHeader = (tests & HeaderTest.Header) != 0;`
|  | `928` |  | `bool testData = (tests & HeaderTest.Extract) != 0;`
|  | `929` |  | ``
|  | `930` |  | `baseStream_.Seek(offsetOfFirstEntry + entry.Offset, SeekOrigin.Begin);`
|  | `931` |  | `if ((int)ReadLEUint() != ZipConstants.LocalHeaderSignature) {`
|  | `932` |  | `throw new ZipException(string.Format("Wrong local header signature @{0:X}", offsetOfFirstEntry + entry.Offset)`
|  | `933` |  | `}`
|  | `934` |  | ``
|  | `935` |  | `var extractVersion = (short)(ReadLEUshort() & 0x00ff);`
|  | `936` |  | `var localFlags = (short)ReadLEUshort();`
|  | `937` |  | `var compressionMethod = (short)ReadLEUshort();`
|  | `938` |  | `var fileTime = (short)ReadLEUshort();`
|  | `939` |  | `var fileDate = (short)ReadLEUshort();`
|  | `940` |  | `uint crcValue = ReadLEUint();`
|  | `941` |  | `long compressedSize = ReadLEUint();`
|  | `942` |  | `long size = ReadLEUint();`
|  | `943` |  | `int storedNameLength = ReadLEUshort();`
|  | `944` |  | `int extraDataLength = ReadLEUshort();`
|  | `945` |  | ``
|  | `946` |  | `byte[] nameData = new byte[storedNameLength];`
|  | `947` |  | `StreamUtils.ReadFully(baseStream_, nameData);`
|  | `948` |  | ``
|  | `949` |  | `byte[] extraData = new byte[extraDataLength];`
|  | `950` |  | `StreamUtils.ReadFully(baseStream_, extraData);`
|  | `951` |  | ``
|  | `952` |  | `var localExtraData = new ZipExtraData(extraData);`
|  | `953` |  | ``
|  | `954` |  | `// Extra data / zip64 checks`
|  | `955` |  | `if (localExtraData.Find(1)) {`
|  | `956` |  | `// 2010-03-04 Forum 10512: removed checks for version >= ZipConstants.VersionZip64`
|  | `957` |  | `// and size or compressedSize = MaxValue, due to rogue creators.`
|  | `958` |  | ``
|  | `959` |  | `size = localExtraData.ReadLong();`
|  | `960` |  | `compressedSize = localExtraData.ReadLong();`
|  | `961` |  | ``
|  | `962` |  | `if ((localFlags & (int)GeneralBitFlags.Descriptor) != 0) {`
|  | `963` |  | `// These may be valid if patched later`
|  | `964` |  | `if ((size != -1) && (size != entry.Size)) {`
|  | `965` |  | `throw new ZipException("Size invalid for descriptor");`
|  | `966` |  | `}`
|  | `967` |  | ``
|  | `968` |  | `if ((compressedSize != -1) && (compressedSize != entry.CompressedSize)) {`
|  | `969` |  | `throw new ZipException("Compressed size invalid for descriptor");`
|  | `970` |  | `}`
|  | `971` |  | `}`
|  | `972` |  | `} else {`
|  | `973` |  | `// No zip64 extra data but entry requires it.`
|  | `974` |  | `if ((extractVersion >= ZipConstants.VersionZip64) &&`
|  | `975` |  | `(((uint)size == uint.MaxValue) || ((uint)compressedSize == uint.MaxValue))) {`
|  | `976` |  | `throw new ZipException("Required Zip64 extended information missing");`
|  | `977` |  | `}`
|  | `978` |  | `}`
|  | `979` |  | ``
|  | `980` |  | `if (testData) {`
|  | `981` |  | `if (entry.IsFile) {`
|  | `982` |  | `if (!entry.IsCompressionMethodSupported()) {`
|  | `983` |  | `throw new ZipException("Compression method not supported");`
|  | `984` |  | `}`
|  | `985` |  | ``
|  | `986` |  | `if ((extractVersion > ZipConstants.VersionMadeBy)`
|  | `987` |  | `|| ((extractVersion > 20) && (extractVersion < ZipConstants.VersionZip64))) {`
|  | `988` |  | `throw new ZipException(string.Format("Version required to extract this entry not supported ({0})", extract`
|  | `989` |  | `}`
|  | `990` |  | ``
|  | `991` |  | `if ((localFlags & (int)(GeneralBitFlags.Patched | GeneralBitFlags.StrongEncryption | GeneralBitFlags.Enhance`
|  | `992` |  | `throw new ZipException("The library does not support the zip version required to extract this entry");`
|  | `993` |  | `}`
|  | `994` |  | `}`
|  | `995` |  | `}`
|  | `996` |  | ``
|  | `997` |  | `if (testHeader) {`
|  | `998` |  | `if ((extractVersion <= 63) && // Ignore later versions as we dont know about them..`
|  | `999` |  | `(extractVersion != 10) &&`
|  | `1000` |  | `(extractVersion != 11) &&`
|  | `1001` |  | `(extractVersion != 20) &&`
|  | `1002` |  | `(extractVersion != 21) &&`
|  | `1003` |  | `(extractVersion != 25) &&`
|  | `1004` |  | `(extractVersion != 27) &&`
|  | `1005` |  | `(extractVersion != 45) &&`
|  | `1006` |  | `(extractVersion != 46) &&`
|  | `1007` |  | `(extractVersion != 50) &&`
|  | `1008` |  | `(extractVersion != 51) &&`
|  | `1009` |  | `(extractVersion != 52) &&`
|  | `1010` |  | `(extractVersion != 61) &&`
|  | `1011` |  | `(extractVersion != 62) &&`
|  | `1012` |  | `(extractVersion != 63)`
|  | `1013` |  | `) {`
|  | `1014` |  | `throw new ZipException(string.Format("Version required to extract this entry is invalid ({0})", extractVersi`
|  | `1015` |  | `}`
|  | `1016` |  | ``
|  | `1017` |  | `// Local entry flags dont have reserved bit set on.`
|  | `1018` |  | `if ((localFlags & (int)(GeneralBitFlags.ReservedPKware4 | GeneralBitFlags.ReservedPkware14 | GeneralBitFlags.R`
|  | `1019` |  | `throw new ZipException("Reserved bit flags cannot be set.");`
|  | `1020` |  | `}`
|  | `1021` |  | ``
|  | `1022` |  | `// Encryption requires extract version >= 20`
|  | `1023` |  | `if (((localFlags & (int)GeneralBitFlags.Encrypted) != 0) && (extractVersion < 20)) {`
|  | `1024` |  | `throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0})`
|  | `1025` |  | `}`
|  | `1026` |  | ``
|  | `1027` |  | `// Strong encryption requires encryption flag to be set and extract version >= 50.`
|  | `1028` |  | `if ((localFlags & (int)GeneralBitFlags.StrongEncryption) != 0) {`
|  | `1029` |  | `if ((localFlags & (int)GeneralBitFlags.Encrypted) == 0) {`
|  | `1030` |  | `throw new ZipException("Strong encryption flag set but encryption flag is not set");`
|  | `1031` |  | `}`
|  | `1032` |  | ``
|  | `1033` |  | `if (extractVersion < 50) {`
|  | `1034` |  | `throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0`
|  | `1035` |  | `}`
|  | `1036` |  | `}`
|  | `1037` |  | ``
|  | `1038` |  | `// Patched entries require extract version >= 27`
|  | `1039` |  | `if (((localFlags & (int)GeneralBitFlags.Patched) != 0) && (extractVersion < 27)) {`
|  | `1040` |  | `throw new ZipException(string.Format("Patched data requires higher version than ({0})", extractVersion));`
|  | `1041` |  | `}`
|  | `1042` |  | ``
|  | `1043` |  | `// Central header flags match local entry flags.`
|  | `1044` |  | `if (localFlags != entry.Flags) {`
|  | `1045` |  | `throw new ZipException("Central header/local header flags mismatch");`
|  | `1046` |  | `}`
|  | `1047` |  | ``
|  | `1048` |  | `// Central header compression method matches local entry`
|  | `1049` |  | `if (entry.CompressionMethod != (CompressionMethod)compressionMethod) {`
|  | `1050` |  | `throw new ZipException("Central header/local header compression method mismatch");`
|  | `1051` |  | `}`
|  | `1052` |  | ``
|  | `1053` |  | `if (entry.Version != extractVersion) {`
|  | `1054` |  | `throw new ZipException("Extract version mismatch");`
|  | `1055` |  | `}`
|  | `1056` |  | ``
|  | `1057` |  | `// Strong encryption and extract version match`
|  | `1058` |  | `if ((localFlags & (int)GeneralBitFlags.StrongEncryption) != 0) {`
|  | `1059` |  | `if (extractVersion < 62) {`
|  | `1060` |  | `throw new ZipException("Strong encryption flag set but version not high enough");`
|  | `1061` |  | `}`
|  | `1062` |  | `}`
|  | `1063` |  | ``
|  | `1064` |  | `if ((localFlags & (int)GeneralBitFlags.HeaderMasked) != 0) {`
|  | `1065` |  | `if ((fileTime != 0) || (fileDate != 0)) {`
|  | `1066` |  | `throw new ZipException("Header masked set but date/time values non-zero");`
|  | `1067` |  | `}`
|  | `1068` |  | `}`
|  | `1069` |  | ``
|  | `1070` |  | `if ((localFlags & (int)GeneralBitFlags.Descriptor) == 0) {`
|  | `1071` |  | `if (crcValue != (uint)entry.Crc) {`
|  | `1072` |  | `throw new ZipException("Central header/local header crc mismatch");`
|  | `1073` |  | `}`
|  | `1074` |  | `}`
|  | `1075` |  | ``
|  | `1076` |  | `// Crc valid for empty entry.`
|  | `1077` |  | `// This will also apply to streamed entries where size isnt known and the header cant be patched`
|  | `1078` |  | `if ((size == 0) && (compressedSize == 0)) {`
|  | `1079` |  | `if (crcValue != 0) {`
|  | `1080` |  | `throw new ZipException("Invalid CRC for empty entry");`
|  | `1081` |  | `}`
|  | `1082` |  | `}`
|  | `1083` |  | ``
|  | `1084` |  | `// TODO: make test more correct... can't compare lengths as was done originally as this can fail for MBCS str`
|  | `1085` |  | `// Assuming a code page at this point is not valid? Best is to store the name length in the ZipEntry probably`
|  | `1086` |  | `if (entry.Name.Length > storedNameLength) {`
|  | `1087` |  | `throw new ZipException("File name length mismatch");`
|  | `1088` |  | `}`
|  | `1089` |  | ``
|  | `1090` |  | `// Name data has already been read convert it and compare.`
|  | `1091` |  | `string localName = ZipConstants.ConvertToStringExt(localFlags, nameData);`
|  | `1092` |  | ``
|  | `1093` |  | `// Central directory and local entry name match`
|  | `1094` |  | `if (localName != entry.Name) {`
|  | `1095` |  | `throw new ZipException("Central header and local header file name mismatch");`
|  | `1096` |  | `}`
|  | `1097` |  | ``
|  | `1098` |  | `// Directories have zero actual size but can have compressed size`
|  | `1099` |  | `if (entry.IsDirectory) {`
|  | `1100` |  | `if (size > 0) {`
|  | `1101` |  | `throw new ZipException("Directory cannot have size");`
|  | `1102` |  | `}`
|  | `1103` |  | ``
|  | `1104` |  | `// There may be other cases where the compressed size can be greater than this?`
|  | `1105` |  | `// If so until details are known we will be strict.`
|  | `1106` |  | `if (entry.IsCrypted) {`
|  | `1107` |  | `if (compressedSize > ZipConstants.CryptoHeaderSize + 2) {`
|  | `1108` |  | `throw new ZipException("Directory compressed size invalid");`
|  | `1109` |  | `}`
|  | `1110` |  | `} else if (compressedSize > 2) {`
|  | `1111` |  | `// When not compressed the directory size can validly be 2 bytes`
|  | `1112` |  | `// if the true size wasnt known when data was originally being written.`
|  | `1113` |  | `// NOTE: Versions of the library 0.85.4 and earlier always added 2 bytes`
|  | `1114` |  | `throw new ZipException("Directory compressed size invalid");`
|  | `1115` |  | `}`
|  | `1116` |  | `}`
|  | `1117` |  | ``
|  | `1118` |  | `if (!ZipNameTransform.IsValidName(localName, true)) {`
|  | `1119` |  | `throw new ZipException("Name is invalid");`
|  | `1120` |  | `}`
|  | `1121` |  | `}`
|  | `1122` |  | ``
|  | `1123` |  | `// Tests that apply to both data and header.`
|  | `1124` |  | ``
|  | `1125` |  | `// Size can be verified only if it is known in the local header.`
|  | `1126` |  | `// it will always be known in the central header.`
|  | `1127` |  | `if (((localFlags & (int)GeneralBitFlags.Descriptor) == 0) ||`
|  | `1128` |  | `((size > 0 || compressedSize > 0) && entry.Size > 0)) {`
|  | `1129` |  | ``
|  | `1130` |  | `if ((size != 0)`
|  | `1131` |  | `&& (size != entry.Size)) {`
|  | `1132` |  | `throw new ZipException(`
|  | `1133` |  | `string.Format("Size mismatch between central header({0}) and local header({1})",`
|  | `1134` |  | `entry.Size, size));`
|  | `1135` |  | `}`
|  | `1136` |  | ``
|  | `1137` |  | `if ((compressedSize != 0)`
|  | `1138` |  | `&& (compressedSize != entry.CompressedSize && compressedSize != 0xFFFFFFFF && compressedSize != -1)) {`
|  | `1139` |  | `throw new ZipException(`
|  | `1140` |  | `string.Format("Compressed size mismatch between central header({0}) and local header({1})",`
|  | `1141` |  | `entry.CompressedSize, compressedSize));`
|  | `1142` |  | `}`
|  | `1143` |  | `}`
|  | `1144` |  | ``
|  | `1145` |  | `int extraLength = storedNameLength + extraDataLength;`
|  | `1146` |  | `return offsetOfFirstEntry + entry.Offset + ZipConstants.LocalHeaderBaseSize + extraLength;`
|  | `1147` |  | `}`
|  | `1148` |  | `}`
|  | `1149` |  | ``
|  | `1150` |  | `#endregion`
|  | `1151` |  | ``
|  | `1152` |  | `#region Updating`
|  | `1153` |  | ``
|  | `1154` |  | `const int DefaultBufferSize = 4096;`
|  | `1155` |  | ``
|  | `1156` |  | `/// <summary>`
|  | `1157` |  | `/// The kind of update to apply.`
|  | `1158` |  | `/// </summary>`
|  | `1159` |  | `enum UpdateCommand`
|  | `1160` |  | `{`
|  | `1161` |  | `Copy, // Copy original file contents.`
|  | `1162` |  | `Modify, // Change encryption, compression, attributes, name, time etc, of an existing file.`
|  | `1163` |  | `Add, // Add a new file to the archive.`
|  | `1164` |  | `}`
|  | `1165` |  | ``
|  | `1166` |  | `#region Properties`
|  | `1167` |  | `/// <summary>`
|  | `1168` |  | `/// Get / set the <see cref="INameTransform"/> to apply to names when updating.`
|  | `1169` |  | `/// </summary>`
|  | `1170` |  | `public INameTransform NameTransform {`
|  | `1171` |  | `get {`
|  | `1172` |  | `return updateEntryFactory_.NameTransform;`
|  | `1173` |  | `}`
|  | `1174` |  | ``
|  | `1175` |  | `set {`
|  | `1176` |  | `updateEntryFactory_.NameTransform = value;`
|  | `1177` |  | `}`
|  | `1178` |  | `}`
|  | `1179` |  | ``
|  | `1180` |  | `/// <summary>`
|  | `1181` |  | `/// Get/set the <see cref="IEntryFactory"/> used to generate <see cref="ZipEntry"/> values`
|  | `1182` |  | `/// during updates.`
|  | `1183` |  | `/// </summary>`
|  | `1184` |  | `public IEntryFactory EntryFactory {`
|  | `1185` |  | `get {`
|  | `1186` |  | `return updateEntryFactory_;`
|  | `1187` |  | `}`
|  | `1188` |  | ``
|  | `1189` |  | `set {`
|  | `1190` |  | `if (value == null) {`
|  | `1191` |  | `updateEntryFactory_ = new ZipEntryFactory();`
|  | `1192` |  | `} else {`
|  | `1193` |  | `updateEntryFactory_ = value;`
|  | `1194` |  | `}`
|  | `1195` |  | `}`
|  | `1196` |  | `}`
|  | `1197` |  | ``
|  | `1198` |  | `/// <summary>`
|  | `1199` |  | `/// Get /set the buffer size to be used when updating this zip file.`
|  | `1200` |  | `/// </summary>`
|  | `1201` |  | `public int BufferSize {`
|  | `1202` |  | `get { return bufferSize_; }`
|  | `1203` |  | `set {`
|  | `1204` |  | `if (value < 1024) {`
|  | `1205` |  | `throw new ArgumentOutOfRangeException(nameof(value), "cannot be below 1024");`
|  | `1206` |  | `}`
|  | `1207` |  | ``
|  | `1208` |  | `if (bufferSize_ != value) {`
|  | `1209` |  | `bufferSize_ = value;`
|  | `1210` |  | `copyBuffer_ = null;`
|  | `1211` |  | `}`
|  | `1212` |  | `}`
|  | `1213` |  | `}`
|  | `1214` |  | ``
|  | `1215` |  | `/// <summary>`
|  | `1216` |  | `/// Get a value indicating an update has <see cref="BeginUpdate()">been started</see>.`
|  | `1217` |  | `/// </summary>`
|  | `1218` |  | `public bool IsUpdating {`
|  | `1219` |  | `get { return updates_ != null; }`
|  | `1220` |  | `}`
|  | `1221` |  | ``
|  | `1222` |  | `/// <summary>`
|  | `1223` |  | `/// Get / set a value indicating how Zip64 Extension usage is determined when adding entries.`
|  | `1224` |  | `/// </summary>`
|  | `1225` |  | `public UseZip64 UseZip64 {`
|  | `1226` |  | `get { return useZip64_; }`
|  | `1227` |  | `set { useZip64_ = value; }`
|  | `1228` |  | `}`
|  | `1229` |  | ``
|  | `1230` |  | `#endregion`
|  | `1231` |  | ``
|  | `1232` |  | `#region Immediate updating`
|  | `1233` |  | `// TBD: Direct form of updating`
|  | `1234` |  | `//`
|  | `1235` |  | `// public void Update(IEntryMatcher deleteMatcher)`
|  | `1236` |  | `// {`
|  | `1237` |  | `// }`
|  | `1238` |  | `//`
|  | `1239` |  | `// public void Update(IScanner addScanner)`
|  | `1240` |  | `// {`
|  | `1241` |  | `// }`
|  | `1242` |  | `#endregion`
|  | `1243` |  | ``
|  | `1244` |  | `#region Deferred Updating`
|  | `1245` |  | `/// <summary>`
|  | `1246` |  | `/// Begin updating this <see cref="ZipFile"/> archive.`
|  | `1247` |  | `/// </summary>`
|  | `1248` |  | `/// <param name="archiveStorage">The <see cref="IArchiveStorage">archive storage</see> for use during the update.</p`
|  | `1249` |  | `/// <param name="dataSource">The <see cref="IDynamicDataSource">data source</see> to utilise during updating.</param`
|  | `1250` |  | `/// <exception cref="ObjectDisposedException">ZipFile has been closed.</exception>`
|  | `1251` |  | `/// <exception cref="ArgumentNullException">One of the arguments provided is null</exception>`
|  | `1252` |  | `/// <exception cref="ObjectDisposedException">ZipFile has been closed.</exception>`
|  | `1253` |  | `public void BeginUpdate(IArchiveStorage archiveStorage, IDynamicDataSource dataSource)`
|  | `1254` |  | `{`
|  | `1255` |  | `if (archiveStorage == null) {`
|  | `1256` |  | `throw new ArgumentNullException(nameof(archiveStorage));`
|  | `1257` |  | `}`
|  | `1258` |  | ``
|  | `1259` |  | `if (dataSource == null) {`
|  | `1260` |  | `throw new ArgumentNullException(nameof(dataSource));`
|  | `1261` |  | `}`
|  | `1262` |  | ``
|  | `1263` |  | `if (isDisposed_) {`
|  | `1264` |  | `throw new ObjectDisposedException("ZipFile");`
|  | `1265` |  | `}`
|  | `1266` |  | ``
|  | `1267` |  | `if (IsEmbeddedArchive) {`
|  | `1268` |  | `throw new ZipException("Cannot update embedded/SFX archives");`
|  | `1269` |  | `}`
|  | `1270` |  | ``
|  | `1271` |  | `archiveStorage_ = archiveStorage;`
|  | `1272` |  | `updateDataSource_ = dataSource;`
|  | `1273` |  | ``
|  | `1274` |  | `// NOTE: the baseStream_ may not currently support writing or seeking.`
|  | `1275` |  | ``
|  | `1276` |  | `updateIndex_ = new Hashtable();`
|  | `1277` |  | ``
|  | `1278` |  | `updates_ = new ArrayList(entries_.Length);`
|  | `1279` |  | `foreach (ZipEntry entry in entries_) {`
|  | `1280` |  | `int index = updates_.Add(new ZipUpdate(entry));`
|  | `1281` |  | `updateIndex_.Add(entry.Name, index);`
|  | `1282` |  | `}`
|  | `1283` |  | ``
|  | `1284` |  | `// We must sort by offset before using offset's calculated sizes`
|  | `1285` |  | `updates_.Sort(new UpdateComparer());`
|  | `1286` |  | ``
|  | `1287` |  | `int idx = 0;`
|  | `1288` |  | `foreach (ZipUpdate update in updates_) {`
|  | `1289` |  | `//If last entry, there is no next entry offset to use`
|  | `1290` |  | `if (idx == updates_.Count - 1)`
|  | `1291` |  | `break;`
|  | `1292` |  | ``
|  | `1293` |  | `update.OffsetBasedSize = ((ZipUpdate)updates_[idx + 1]).Entry.Offset - update.Entry.Offset;`
|  | `1294` |  | `idx++;`
|  | `1295` |  | `}`
|  | `1296` |  | `updateCount_ = updates_.Count;`
|  | `1297` |  | ``
|  | `1298` |  | `contentsEdited_ = false;`
|  | `1299` |  | `commentEdited_ = false;`
|  | `1300` |  | `newComment_ = null;`
|  | `1301` |  | `}`
|  | `1302` |  | ``
|  | `1303` |  | `/// <summary>`
|  | `1304` |  | `/// Begin updating to this <see cref="ZipFile"/> archive.`
|  | `1305` |  | `/// </summary>`
|  | `1306` |  | `/// <param name="archiveStorage">The storage to use during the update.</param>`
|  | `1307` |  | `public void BeginUpdate(IArchiveStorage archiveStorage)`
|  | `1308` |  | `{`
|  | `1309` |  | `BeginUpdate(archiveStorage, new DynamicDiskDataSource());`
|  | `1310` |  | `}`
|  | `1311` |  | ``
|  | `1312` |  | `/// <summary>`
|  | `1313` |  | `/// Begin updating this <see cref="ZipFile"/> archive.`
|  | `1314` |  | `/// </summary>`
|  | `1315` |  | `/// <seealso cref="BeginUpdate(IArchiveStorage)"/>`
|  | `1316` |  | `/// <seealso cref="CommitUpdate"></seealso>`
|  | `1317` |  | `/// <seealso cref="AbortUpdate"></seealso>`
|  | `1318` |  | `public void BeginUpdate()`
|  | `1319` |  | `{`
|  | `1320` |  | `if (Name == null) {`
|  | `1321` |  | `BeginUpdate(new MemoryArchiveStorage(), new DynamicDiskDataSource());`
|  | `1322` |  | `} else {`
|  | `1323` |  | `BeginUpdate(new DiskArchiveStorage(this), new DynamicDiskDataSource());`
|  | `1324` |  | `}`
|  | `1325` |  | `}`
|  | `1326` |  | ``
|  | `1327` |  | `/// <summary>`
|  | `1328` |  | `/// Commit current updates, updating this archive.`
|  | `1329` |  | `/// </summary>`
|  | `1330` |  | `/// <seealso cref="BeginUpdate()"></seealso>`
|  | `1331` |  | `/// <seealso cref="AbortUpdate"></seealso>`
|  | `1332` |  | `/// <exception cref="ObjectDisposedException">ZipFile has been closed.</exception>`
|  | `1333` |  | `public void CommitUpdate()`
|  | `1334` |  | `{`
|  | `1335` |  | `if (isDisposed_) {`
|  | `1336` |  | `throw new ObjectDisposedException("ZipFile");`
|  | `1337` |  | `}`
|  | `1338` |  | ``
|  | `1339` |  | `CheckUpdating();`
|  | `1340` |  | ``
|  | `1341` |  | `try {`
|  | `1342` |  | `updateIndex_.Clear();`
|  | `1343` |  | `updateIndex_ = null;`
|  | `1344` |  | ``
|  | `1345` |  | `if (contentsEdited_) {`
|  | `1346` |  | `RunUpdates();`
|  | `1347` |  | `} else if (commentEdited_) {`
|  | `1348` |  | `UpdateCommentOnly();`
|  | `1349` |  | `} else {`
|  | `1350` |  | `// Create an empty archive if none existed originally.`
|  | `1351` |  | `if (entries_.Length == 0) {`
|  | `1352` |  | `byte[] theComment = (newComment_ != null) ? newComment_.RawComment : ZipConstants.ConvertToArray(comment_);`
|  | `1353` |  | `using (ZipHelperStream zhs = new ZipHelperStream(baseStream_)) {`
|  | `1354` |  | `zhs.WriteEndOfCentralDirectory(0, 0, 0, theComment);`
|  | `1355` |  | `}`
|  | `1356` |  | `}`
|  | `1357` |  | `}`
|  | `1358` |  | ``
|  | `1359` |  | `} finally {`
|  | `1360` |  | `PostUpdateCleanup();`
|  | `1361` |  | `}`
|  | `1362` |  | `}`
|  | `1363` |  | ``
|  | `1364` |  | `/// <summary>`
|  | `1365` |  | `/// Abort updating leaving the archive unchanged.`
|  | `1366` |  | `/// </summary>`
|  | `1367` |  | `/// <seealso cref="BeginUpdate()"></seealso>`
|  | `1368` |  | `/// <seealso cref="CommitUpdate"></seealso>`
|  | `1369` |  | `public void AbortUpdate()`
|  | `1370` |  | `{`
|  | `1371` |  | `PostUpdateCleanup();`
|  | `1372` |  | `}`
|  | `1373` |  | ``
|  | `1374` |  | `/// <summary>`
|  | `1375` |  | `/// Set the file comment to be recorded when the current update is <see cref="CommitUpdate">commited</see>.`
|  | `1376` |  | `/// </summary>`
|  | `1377` |  | `/// <param name="comment">The comment to record.</param>`
|  | `1378` |  | `/// <exception cref="ObjectDisposedException">ZipFile has been closed.</exception>`
|  | `1379` |  | `public void SetComment(string comment)`
|  | `1380` |  | `{`
|  | `1381` |  | `if (isDisposed_) {`
|  | `1382` |  | `throw new ObjectDisposedException("ZipFile");`
|  | `1383` |  | `}`
|  | `1384` |  | ``
|  | `1385` |  | `CheckUpdating();`
|  | `1386` |  | ``
|  | `1387` |  | `newComment_ = new ZipString(comment);`
|  | `1388` |  | ``
|  | `1389` |  | `if (newComment_.RawLength > 0xffff) {`
|  | `1390` |  | `newComment_ = null;`
|  | `1391` |  | `throw new ZipException("Comment length exceeds maximum - 65535");`
|  | `1392` |  | `}`
|  | `1393` |  | ``
|  | `1394` |  | `// We dont take account of the original and current comment appearing to be the same`
|  | `1395` |  | `// as encoding may be different.`
|  | `1396` |  | `commentEdited_ = true;`
|  | `1397` |  | `}`
|  | `1398` |  | ``
|  | `1399` |  | `#endregion`
|  | `1400` |  | ``
|  | `1401` |  | `#region Adding Entries`
|  | `1402` |  | ``
|  | `1403` |  | `void AddUpdate(ZipUpdate update)`
|  | `1404` |  | `{`
|  | `1405` |  | `contentsEdited_ = true;`
|  | `1406` |  | ``
|  | `1407` |  | `int index = FindExistingUpdate(update.Entry.Name);`
|  | `1408` |  | ``
|  | `1409` |  | `if (index >= 0) {`
|  | `1410` |  | `if (updates_[index] == null) {`
|  | `1411` |  | `updateCount_ += 1;`
|  | `1412` |  | `}`
|  | `1413` |  | ``
|  | `1414` |  | `// Direct replacement is faster than delete and add.`
|  | `1415` |  | `updates_[index] = update;`
|  | `1416` |  | `} else {`
|  | `1417` |  | `index = updates_.Add(update);`
|  | `1418` |  | `updateCount_ += 1;`
|  | `1419` |  | `updateIndex_.Add(update.Entry.Name, index);`
|  | `1420` |  | `}`
|  | `1421` |  | `}`
|  | `1422` |  | ``
|  | `1423` |  | `/// <summary>`
|  | `1424` |  | `/// Add a new entry to the archive.`
|  | `1425` |  | `/// </summary>`
|  | `1426` |  | `/// <param name="fileName">The name of the file to add.</param>`
|  | `1427` |  | `/// <param name="compressionMethod">The compression method to use.</param>`
|  | `1428` |  | `/// <param name="useUnicodeText">Ensure Unicode text is used for name and comment for this entry.</param>`
|  | `1429` |  | `/// <exception cref="ArgumentNullException">Argument supplied is null.</exception>`
|  | `1430` |  | `/// <exception cref="ObjectDisposedException">ZipFile has been closed.</exception>`
|  | `1431` |  | `/// <exception cref="ArgumentOutOfRangeException">Compression method is not supported.</exception>`
|  | `1432` |  | `public void Add(string fileName, CompressionMethod compressionMethod, bool useUnicodeText)`
|  | `1433` |  | `{`
|  | `1434` |  | `if (fileName == null) {`
|  | `1435` |  | `throw new ArgumentNullException(nameof(fileName));`
|  | `1436` |  | `}`
|  | `1437` |  | ``
|  | `1438` |  | `if (isDisposed_) {`
|  | `1439` |  | `throw new ObjectDisposedException("ZipFile");`
|  | `1440` |  | `}`
|  | `1441` |  | ``
|  | `1442` |  | `if (!ZipEntry.IsCompressionMethodSupported(compressionMethod)) {`
|  | `1443` |  | `throw new ArgumentOutOfRangeException(nameof(compressionMethod));`
|  | `1444` |  | `}`
|  | `1445` |  | ``
|  | `1446` |  | `CheckUpdating();`
|  | `1447` |  | `contentsEdited_ = true;`
|  | `1448` |  | ``
|  | `1449` |  | `ZipEntry entry = EntryFactory.MakeFileEntry(fileName);`
|  | `1450` |  | `entry.IsUnicodeText = useUnicodeText;`
|  | `1451` |  | `entry.CompressionMethod = compressionMethod;`
|  | `1452` |  | ``
|  | `1453` |  | `AddUpdate(new ZipUpdate(fileName, entry));`
|  | `1454` |  | `}`
|  | `1455` |  | ``
|  | `1456` |  | `/// <summary>`
|  | `1457` |  | `/// Add a new entry to the archive.`
|  | `1458` |  | `/// </summary>`
|  | `1459` |  | `/// <param name="fileName">The name of the file to add.</param>`
|  | `1460` |  | `/// <param name="compressionMethod">The compression method to use.</param>`
|  | `1461` |  | `/// <exception cref="ArgumentNullException">ZipFile has been closed.</exception>`
|  | `1462` |  | `/// <exception cref="ArgumentOutOfRangeException">The compression method is not supported.</exception>`
|  | `1463` |  | `public void Add(string fileName, CompressionMethod compressionMethod)`
|  | `1464` |  | `{`
|  | `1465` |  | `if (fileName == null) {`
|  | `1466` |  | `throw new ArgumentNullException(nameof(fileName));`
|  | `1467` |  | `}`
|  | `1468` |  | ``
|  | `1469` |  | `if (!ZipEntry.IsCompressionMethodSupported(compressionMethod)) {`
|  | `1470` |  | `throw new ArgumentOutOfRangeException(nameof(compressionMethod));`
|  | `1471` |  | `}`
|  | `1472` |  | ``
|  | `1473` |  | `CheckUpdating();`
|  | `1474` |  | `contentsEdited_ = true;`
|  | `1475` |  | ``
|  | `1476` |  | `ZipEntry entry = EntryFactory.MakeFileEntry(fileName);`
|  | `1477` |  | `entry.CompressionMethod = compressionMethod;`
|  | `1478` |  | `AddUpdate(new ZipUpdate(fileName, entry));`
|  | `1479` |  | `}`
|  | `1480` |  | ``
|  | `1481` |  | `/// <summary>`
|  | `1482` |  | `/// Add a file to the archive.`
|  | `1483` |  | `/// </summary>`
|  | `1484` |  | `/// <param name="fileName">The name of the file to add.</param>`
|  | `1485` |  | `/// <exception cref="ArgumentNullException">Argument supplied is null.</exception>`
|  | `1486` |  | `public void Add(string fileName)`
|  | `1487` |  | `{`
|  | `1488` |  | `if (fileName == null) {`
|  | `1489` |  | `throw new ArgumentNullException(nameof(fileName));`
|  | `1490` |  | `}`
|  | `1491` |  | ``
|  | `1492` |  | `CheckUpdating();`
|  | `1493` |  | `AddUpdate(new ZipUpdate(fileName, EntryFactory.MakeFileEntry(fileName)));`
|  | `1494` |  | `}`
|  | `1495` |  | ``
|  | `1496` |  | `/// <summary>`
|  | `1497` |  | `/// Add a file to the archive.`
|  | `1498` |  | `/// </summary>`
|  | `1499` |  | `/// <param name="fileName">The name of the file to add.</param>`
|  | `1500` |  | `/// <param name="entryName">The name to use for the <see cref="ZipEntry"/> on the Zip file created.</param>`
|  | `1501` |  | `/// <exception cref="ArgumentNullException">Argument supplied is null.</exception>`
|  | `1502` |  | `public void Add(string fileName, string entryName)`
|  | `1503` |  | `{`
|  | `1504` |  | `if (fileName == null) {`
|  | `1505` |  | `throw new ArgumentNullException(nameof(fileName));`
|  | `1506` |  | `}`
|  | `1507` |  | ``
|  | `1508` |  | `if (entryName == null) {`
|  | `1509` |  | `throw new ArgumentNullException(nameof(entryName));`
|  | `1510` |  | `}`
|  | `1511` |  | ``
|  | `1512` |  | `CheckUpdating();`
|  | `1513` |  | `AddUpdate(new ZipUpdate(fileName, EntryFactory.MakeFileEntry(fileName, entryName, true)));`
|  | `1514` |  | `}`
|  | `1515` |  | ``
|  | `1516` |  | ``
|  | `1517` |  | `/// <summary>`
|  | `1518` |  | `/// Add a file entry with data.`
|  | `1519` |  | `/// </summary>`
|  | `1520` |  | `/// <param name="dataSource">The source of the data for this entry.</param>`
|  | `1521` |  | `/// <param name="entryName">The name to give to the entry.</param>`
|  | `1522` |  | `public void Add(IStaticDataSource dataSource, string entryName)`
|  | `1523` |  | `{`
|  | `1524` |  | `if (dataSource == null) {`
|  | `1525` |  | `throw new ArgumentNullException(nameof(dataSource));`
|  | `1526` |  | `}`
|  | `1527` |  | ``
|  | `1528` |  | `if (entryName == null) {`
|  | `1529` |  | `throw new ArgumentNullException(nameof(entryName));`
|  | `1530` |  | `}`
|  | `1531` |  | ``
|  | `1532` |  | `CheckUpdating();`
|  | `1533` |  | `AddUpdate(new ZipUpdate(dataSource, EntryFactory.MakeFileEntry(entryName, false)));`
|  | `1534` |  | `}`
|  | `1535` |  | ``
|  | `1536` |  | `/// <summary>`
|  | `1537` |  | `/// Add a file entry with data.`
|  | `1538` |  | `/// </summary>`
|  | `1539` |  | `/// <param name="dataSource">The source of the data for this entry.</param>`
|  | `1540` |  | `/// <param name="entryName">The name to give to the entry.</param>`
|  | `1541` |  | `/// <param name="compressionMethod">The compression method to use.</param>`
|  | `1542` |  | `public void Add(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod)`
|  | `1543` |  | `{`
|  | `1544` |  | `if (dataSource == null) {`
|  | `1545` |  | `throw new ArgumentNullException(nameof(dataSource));`
|  | `1546` |  | `}`
|  | `1547` |  | ``
|  | `1548` |  | `if (entryName == null) {`
|  | `1549` |  | `throw new ArgumentNullException(nameof(entryName));`
|  | `1550` |  | `}`
|  | `1551` |  | ``
|  | `1552` |  | `CheckUpdating();`
|  | `1553` |  | ``
|  | `1554` |  | `ZipEntry entry = EntryFactory.MakeFileEntry(entryName, false);`
|  | `1555` |  | `entry.CompressionMethod = compressionMethod;`
|  | `1556` |  | ``
|  | `1557` |  | `AddUpdate(new ZipUpdate(dataSource, entry));`
|  | `1558` |  | `}`
|  | `1559` |  | ``
|  | `1560` |  | `/// <summary>`
|  | `1561` |  | `/// Add a file entry with data.`
|  | `1562` |  | `/// </summary>`
|  | `1563` |  | `/// <param name="dataSource">The source of the data for this entry.</param>`
|  | `1564` |  | `/// <param name="entryName">The name to give to the entry.</param>`
|  | `1565` |  | `/// <param name="compressionMethod">The compression method to use.</param>`
|  | `1566` |  | `/// <param name="useUnicodeText">Ensure Unicode text is used for name and comments for this entry.</param>`
|  | `1567` |  | `public void Add(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod, bool useUnicode`
|  | `1568` |  | `{`
|  | `1569` |  | `if (dataSource == null) {`
|  | `1570` |  | `throw new ArgumentNullException(nameof(dataSource));`
|  | `1571` |  | `}`
|  | `1572` |  | ``
|  | `1573` |  | `if (entryName == null) {`
|  | `1574` |  | `throw new ArgumentNullException(nameof(entryName));`
|  | `1575` |  | `}`
|  | `1576` |  | ``
|  | `1577` |  | `CheckUpdating();`
|  | `1578` |  | ``
|  | `1579` |  | `ZipEntry entry = EntryFactory.MakeFileEntry(entryName, false);`
|  | `1580` |  | `entry.IsUnicodeText = useUnicodeText;`
|  | `1581` |  | `entry.CompressionMethod = compressionMethod;`
|  | `1582` |  | ``
|  | `1583` |  | `AddUpdate(new ZipUpdate(dataSource, entry));`
|  | `1584` |  | `}`
|  | `1585` |  | ``
|  | `1586` |  | `/// <summary>`
|  | `1587` |  | `/// Add a <see cref="ZipEntry"/> that contains no data.`
|  | `1588` |  | `/// </summary>`
|  | `1589` |  | `/// <param name="entry">The entry to add.</param>`
|  | `1590` |  | `/// <remarks>This can be used to add directories, volume labels, or empty file entries.</remarks>`
|  | `1591` |  | `public void Add(ZipEntry entry)`
|  | `1592` |  | `{`
|  | `1593` |  | `if (entry == null) {`
|  | `1594` |  | `throw new ArgumentNullException(nameof(entry));`
|  | `1595` |  | `}`
|  | `1596` |  | ``
|  | `1597` |  | `CheckUpdating();`
|  | `1598` |  | ``
|  | `1599` |  | `if ((entry.Size != 0) || (entry.CompressedSize != 0)) {`
|  | `1600` |  | `throw new ZipException("Entry cannot have any data");`
|  | `1601` |  | `}`
|  | `1602` |  | ``
|  | `1603` |  | `AddUpdate(new ZipUpdate(UpdateCommand.Add, entry));`
|  | `1604` |  | `}`
|  | `1605` |  | ``
|  | `1606` |  | `/// <summary>`
|  | `1607` |  | `/// Add a directory entry to the archive.`
|  | `1608` |  | `/// </summary>`
|  | `1609` |  | `/// <param name="directoryName">The directory to add.</param>`
|  | `1610` |  | `public void AddDirectory(string directoryName)`
|  | `1611` |  | `{`
|  | `1612` |  | `if (directoryName == null) {`
|  | `1613` |  | `throw new ArgumentNullException(nameof(directoryName));`
|  | `1614` |  | `}`
|  | `1615` |  | ``
|  | `1616` |  | `CheckUpdating();`
|  | `1617` |  | ``
|  | `1618` |  | `ZipEntry dirEntry = EntryFactory.MakeDirectoryEntry(directoryName);`
|  | `1619` |  | `AddUpdate(new ZipUpdate(UpdateCommand.Add, dirEntry));`
|  | `1620` |  | `}`
|  | `1621` |  | ``
|  | `1622` |  | `#endregion`
|  | `1623` |  | ``
|  | `1624` |  | `#region Modifying Entries`
|  | `1625` |  | `/* Modify not yet ready for public consumption.`
|  | `1626` |  | `Direct modification of an entry should not overwrite original data before its read.`
|  | `1627` |  | `Safe mode is trivial in this sense.`
|  | `1628` |  | `public void Modify(ZipEntry original, ZipEntry updated)`
|  | `1629` |  | `{`
|  | `1630` |  | `if ( original == null ) {`
|  | `1631` |  | `throw new ArgumentNullException("original");`
|  | `1632` |  | `}`
|  | `1633` |  | ``
|  | `1634` |  | `if ( updated == null ) {`
|  | `1635` |  | `throw new ArgumentNullException("updated");`
|  | `1636` |  | `}`
|  | `1637` |  | ``
|  | `1638` |  | `CheckUpdating();`
|  | `1639` |  | `contentsEdited_ = true;`
|  | `1640` |  | `updates_.Add(new ZipUpdate(original, updated));`
|  | `1641` |  | `}`
|  | `1642` |  | `*/`
|  | `1643` |  | `#endregion`
|  | `1644` |  | ``
|  | `1645` |  | `#region Deleting Entries`
|  | `1646` |  | `/// <summary>`
|  | `1647` |  | `/// Delete an entry by name`
|  | `1648` |  | `/// </summary>`
|  | `1649` |  | `/// <param name="fileName">The filename to delete</param>`
|  | `1650` |  | `/// <returns>True if the entry was found and deleted; false otherwise.</returns>`
|  | `1651` |  | `public bool Delete(string fileName)`
|  | `1652` |  | `{`
|  | `1653` |  | `if (fileName == null) {`
|  | `1654` |  | `throw new ArgumentNullException(nameof(fileName));`
|  | `1655` |  | `}`
|  | `1656` |  | ``
|  | `1657` |  | `CheckUpdating();`
|  | `1658` |  | ``
|  | `1659` |  | `bool result = false;`
|  | `1660` |  | `int index = FindExistingUpdate(fileName);`
|  | `1661` |  | `if ((index >= 0) && (updates_[index] != null)) {`
|  | `1662` |  | `result = true;`
|  | `1663` |  | `contentsEdited_ = true;`
|  | `1664` |  | `updates_[index] = null;`
|  | `1665` |  | `updateCount_ -= 1;`
|  | `1666` |  | `} else {`
|  | `1667` |  | `throw new ZipException("Cannot find entry to delete");`
|  | `1668` |  | `}`
|  | `1669` |  | `return result;`
|  | `1670` |  | `}`
|  | `1671` |  | ``
|  | `1672` |  | `/// <summary>`
|  | `1673` |  | `/// Delete a <see cref="ZipEntry"/> from the archive.`
|  | `1674` |  | `/// </summary>`
|  | `1675` |  | `/// <param name="entry">The entry to delete.</param>`
|  | `1676` |  | `public void Delete(ZipEntry entry)`
|  | `1677` |  | `{`
|  | `1678` |  | `if (entry == null) {`
|  | `1679` |  | `throw new ArgumentNullException(nameof(entry));`
|  | `1680` |  | `}`
|  | `1681` |  | ``
|  | `1682` |  | `CheckUpdating();`
|  | `1683` |  | ``
|  | `1684` |  | `int index = FindExistingUpdate(entry);`
|  | `1685` |  | `if (index >= 0) {`
|  | `1686` |  | `contentsEdited_ = true;`
|  | `1687` |  | `updates_[index] = null;`
|  | `1688` |  | `updateCount_ -= 1;`
|  | `1689` |  | `} else {`
|  | `1690` |  | `throw new ZipException("Cannot find entry to delete");`
|  | `1691` |  | `}`
|  | `1692` |  | `}`
|  | `1693` |  | ``
|  | `1694` |  | `#endregion`
|  | `1695` |  | ``
|  | `1696` |  | `#region Update Support`
|  | `1697` |  | ``
|  | `1698` |  | `#region Writing Values/Headers`
|  | `1699` |  | `void WriteLEShort(int value)`
|  | `1700` |  | `{`
|  | `1701` |  | `baseStream_.WriteByte((byte)(value & 0xff));`
|  | `1702` |  | `baseStream_.WriteByte((byte)((value >> 8) & 0xff));`
|  | `1703` |  | `}`
|  | `1704` |  | ``
|  | `1705` |  | `/// <summary>`
|  | `1706` |  | `/// Write an unsigned short in little endian byte order.`
|  | `1707` |  | `/// </summary>`
|  | `1708` |  | `void WriteLEUshort(ushort value)`
|  | `1709` |  | `{`
|  | `1710` |  | `baseStream_.WriteByte((byte)(value & 0xff));`
|  | `1711` |  | `baseStream_.WriteByte((byte)(value >> 8));`
|  | `1712` |  | `}`
|  | `1713` |  | ``
|  | `1714` |  | `/// <summary>`
|  | `1715` |  | `/// Write an int in little endian byte order.`
|  | `1716` |  | `/// </summary>`
|  | `1717` |  | `void WriteLEInt(int value)`
|  | `1718` |  | `{`
|  | `1719` |  | `WriteLEShort(value & 0xffff);`
|  | `1720` |  | `WriteLEShort(value >> 16);`
|  | `1721` |  | `}`
|  | `1722` |  | ``
|  | `1723` |  | `/// <summary>`
|  | `1724` |  | `/// Write an unsigned int in little endian byte order.`
|  | `1725` |  | `/// </summary>`
|  | `1726` |  | `void WriteLEUint(uint value)`
|  | `1727` |  | `{`
|  | `1728` |  | `WriteLEUshort((ushort)(value & 0xffff));`
|  | `1729` |  | `WriteLEUshort((ushort)(value >> 16));`
|  | `1730` |  | `}`
|  | `1731` |  | ``
|  | `1732` |  | `/// <summary>`
|  | `1733` |  | `/// Write a long in little endian byte order.`
|  | `1734` |  | `/// </summary>`
|  | `1735` |  | `void WriteLeLong(long value)`
|  | `1736` |  | `{`
|  | `1737` |  | `WriteLEInt((int)(value & 0xffffffff));`
|  | `1738` |  | `WriteLEInt((int)(value >> 32));`
|  | `1739` |  | `}`
|  | `1740` |  | ``
|  | `1741` |  | `void WriteLEUlong(ulong value)`
|  | `1742` |  | `{`
|  | `1743` |  | `WriteLEUint((uint)(value & 0xffffffff));`
|  | `1744` |  | `WriteLEUint((uint)(value >> 32));`
|  | `1745` |  | `}`
|  | `1746` |  | ``
|  | `1747` |  | `void WriteLocalEntryHeader(ZipUpdate update)`
|  | `1748` |  | `{`
|  | `1749` |  | `ZipEntry entry = update.OutEntry;`
|  | `1750` |  | ``
|  | `1751` |  | `// TODO: Local offset will require adjusting for multi-disk zip files.`
|  | `1752` |  | `entry.Offset = baseStream_.Position;`
|  | `1753` |  | ``
|  | `1754` |  | `// TODO: Need to clear any entry flags that dont make sense or throw an exception here.`
|  | `1755` |  | `if (update.Command != UpdateCommand.Copy) {`
|  | `1756` |  | `if (entry.CompressionMethod == CompressionMethod.Deflated) {`
|  | `1757` |  | `if (entry.Size == 0) {`
|  | `1758` |  | `// No need to compress - no data.`
|  | `1759` |  | `entry.CompressedSize = entry.Size;`
|  | `1760` |  | `entry.Crc = 0;`
|  | `1761` |  | `entry.CompressionMethod = CompressionMethod.Stored;`
|  | `1762` |  | `}`
|  | `1763` |  | `} else if (entry.CompressionMethod == CompressionMethod.Stored) {`
|  | `1764` |  | `entry.Flags &= ~(int)GeneralBitFlags.Descriptor;`
|  | `1765` |  | `}`
|  | `1766` |  | ``
|  | `1767` |  | `if (HaveKeys) {`
|  | `1768` |  | `entry.IsCrypted = true;`
|  | `1769` |  | `if (entry.Crc < 0) {`
|  | `1770` |  | `entry.Flags |= (int)GeneralBitFlags.Descriptor;`
|  | `1771` |  | `}`
|  | `1772` |  | `} else {`
|  | `1773` |  | `entry.IsCrypted = false;`
|  | `1774` |  | `}`
|  | `1775` |  | ``
|  | `1776` |  | `switch (useZip64_) {`
|  | `1777` |  | `case UseZip64.Dynamic:`
|  | `1778` |  | `if (entry.Size < 0) {`
|  | `1779` |  | `entry.ForceZip64();`
|  | `1780` |  | `}`
|  | `1781` |  | `break;`
|  | `1782` |  | ``
|  | `1783` |  | `case UseZip64.On:`
|  | `1784` |  | `entry.ForceZip64();`
|  | `1785` |  | `break;`
|  | `1786` |  | ``
|  | `1787` |  | `case UseZip64.Off:`
|  | `1788` |  | `// Do nothing. The entry itself may be using Zip64 independantly.`
|  | `1789` |  | `break;`
|  | `1790` |  | `}`
|  | `1791` |  | `}`
|  | `1792` |  | ``
|  | `1793` |  | `// Write the local file header`
|  | `1794` |  | `WriteLEInt(ZipConstants.LocalHeaderSignature);`
|  | `1795` |  | ``
|  | `1796` |  | `WriteLEShort(entry.Version);`
|  | `1797` |  | `WriteLEShort(entry.Flags);`
|  | `1798` |  | ``
|  | `1799` |  | `WriteLEShort((byte)entry.CompressionMethod);`
|  | `1800` |  | `WriteLEInt((int)entry.DosTime);`
|  | `1801` |  | ``
|  | `1802` |  | `if (!entry.HasCrc) {`
|  | `1803` |  | `// Note patch address for updating CRC later.`
|  | `1804` |  | `update.CrcPatchOffset = baseStream_.Position;`
|  | `1805` |  | `WriteLEInt((int)0);`
|  | `1806` |  | `} else {`
|  | `1807` |  | `WriteLEInt(unchecked((int)entry.Crc));`
|  | `1808` |  | `}`
|  | `1809` |  | ``
|  | `1810` |  | `if (entry.LocalHeaderRequiresZip64) {`
|  | `1811` |  | `WriteLEInt(-1);`
|  | `1812` |  | `WriteLEInt(-1);`
|  | `1813` |  | `} else {`
|  | `1814` |  | `if ((entry.CompressedSize < 0) || (entry.Size < 0)) {`
|  | `1815` |  | `update.SizePatchOffset = baseStream_.Position;`
|  | `1816` |  | `}`
|  | `1817` |  | ``
|  | `1818` |  | `WriteLEInt((int)entry.CompressedSize);`
|  | `1819` |  | `WriteLEInt((int)entry.Size);`
|  | `1820` |  | `}`
|  | `1821` |  | ``
|  | `1822` |  | `byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);`
|  | `1823` |  | ``
|  | `1824` |  | `if (name.Length > 0xFFFF) {`
|  | `1825` |  | `throw new ZipException("Entry name too long.");`
|  | `1826` |  | `}`
|  | `1827` |  | ``
|  | `1828` |  | `var ed = new ZipExtraData(entry.ExtraData);`
|  | `1829` |  | ``
|  | `1830` |  | `if (entry.LocalHeaderRequiresZip64) {`
|  | `1831` |  | `ed.StartNewEntry();`
|  | `1832` |  | ``
|  | `1833` |  | `// Local entry header always includes size and compressed size.`
|  | `1834` |  | `// NOTE the order of these fields is reversed when compared to the normal headers!`
|  | `1835` |  | `ed.AddLeLong(entry.Size);`
|  | `1836` |  | `ed.AddLeLong(entry.CompressedSize);`
|  | `1837` |  | `ed.AddNewEntry(1);`
|  | `1838` |  | `} else {`
|  | `1839` |  | `ed.Delete(1);`
|  | `1840` |  | `}`
|  | `1841` |  | ``
|  | `1842` |  | `entry.ExtraData = ed.GetEntryData();`
|  | `1843` |  | ``
|  | `1844` |  | `WriteLEShort(name.Length);`
|  | `1845` |  | `WriteLEShort(entry.ExtraData.Length);`
|  | `1846` |  | ``
|  | `1847` |  | `if (name.Length > 0) {`
|  | `1848` |  | `baseStream_.Write(name, 0, name.Length);`
|  | `1849` |  | `}`
|  | `1850` |  | ``
|  | `1851` |  | `if (entry.LocalHeaderRequiresZip64) {`
|  | `1852` |  | `if (!ed.Find(1)) {`
|  | `1853` |  | `throw new ZipException("Internal error cannot find extra data");`
|  | `1854` |  | `}`
|  | `1855` |  | ``
|  | `1856` |  | `update.SizePatchOffset = baseStream_.Position + ed.CurrentReadIndex;`
|  | `1857` |  | `}`
|  | `1858` |  | ``
|  | `1859` |  | `if (entry.ExtraData.Length > 0) {`
|  | `1860` |  | `baseStream_.Write(entry.ExtraData, 0, entry.ExtraData.Length);`
|  | `1861` |  | `}`
|  | `1862` |  | `}`
|  | `1863` |  | ``
|  | `1864` |  | `int WriteCentralDirectoryHeader(ZipEntry entry)`
|  | `1865` |  | `{`
|  | `1866` |  | `if (entry.CompressedSize < 0) {`
|  | `1867` |  | `throw new ZipException("Attempt to write central directory entry with unknown csize");`
|  | `1868` |  | `}`
|  | `1869` |  | ``
|  | `1870` |  | `if (entry.Size < 0) {`
|  | `1871` |  | `throw new ZipException("Attempt to write central directory entry with unknown size");`
|  | `1872` |  | `}`
|  | `1873` |  | ``
|  | `1874` |  | `if (entry.Crc < 0) {`
|  | `1875` |  | `throw new ZipException("Attempt to write central directory entry with unknown crc");`
|  | `1876` |  | `}`
|  | `1877` |  | ``
|  | `1878` |  | `// Write the central file header`
|  | `1879` |  | `WriteLEInt(ZipConstants.CentralHeaderSignature);`
|  | `1880` |  | ``
|  | `1881` |  | `// Version made by`
|  | `1882` |  | `WriteLEShort(ZipConstants.VersionMadeBy);`
|  | `1883` |  | ``
|  | `1884` |  | `// Version required to extract`
|  | `1885` |  | `WriteLEShort(entry.Version);`
|  | `1886` |  | ``
|  | `1887` |  | `WriteLEShort(entry.Flags);`
|  | `1888` |  | ``
|  | `1889` |  | `unchecked {`
|  | `1890` |  | `WriteLEShort((byte)entry.CompressionMethod);`
|  | `1891` |  | `WriteLEInt((int)entry.DosTime);`
|  | `1892` |  | `WriteLEInt((int)entry.Crc);`
|  | `1893` |  | `}`
|  | `1894` |  | ``
|  | `1895` |  | `if ((entry.IsZip64Forced()) || (entry.CompressedSize >= 0xffffffff)) {`
|  | `1896` |  | `WriteLEInt(-1);`
|  | `1897` |  | `} else {`
|  | `1898` |  | `WriteLEInt((int)(entry.CompressedSize & 0xffffffff));`
|  | `1899` |  | `}`
|  | `1900` |  | ``
|  | `1901` |  | `if ((entry.IsZip64Forced()) || (entry.Size >= 0xffffffff)) {`
|  | `1902` |  | `WriteLEInt(-1);`
|  | `1903` |  | `} else {`
|  | `1904` |  | `WriteLEInt((int)entry.Size);`
|  | `1905` |  | `}`
|  | `1906` |  | ``
|  | `1907` |  | `byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);`
|  | `1908` |  | ``
|  | `1909` |  | `if (name.Length > 0xFFFF) {`
|  | `1910` |  | `throw new ZipException("Entry name is too long.");`
|  | `1911` |  | `}`
|  | `1912` |  | ``
|  | `1913` |  | `WriteLEShort(name.Length);`
|  | `1914` |  | ``
|  | `1915` |  | `// Central header extra data is different to local header version so regenerate.`
|  | `1916` |  | `var ed = new ZipExtraData(entry.ExtraData);`
|  | `1917` |  | ``
|  | `1918` |  | `if (entry.CentralHeaderRequiresZip64) {`
|  | `1919` |  | `ed.StartNewEntry();`
|  | `1920` |  | ``
|  | `1921` |  | `if ((entry.Size >= 0xffffffff) || (useZip64_ == UseZip64.On)) {`
|  | `1922` |  | `ed.AddLeLong(entry.Size);`
|  | `1923` |  | `}`
|  | `1924` |  | ``
|  | `1925` |  | `if ((entry.CompressedSize >= 0xffffffff) || (useZip64_ == UseZip64.On)) {`
|  | `1926` |  | `ed.AddLeLong(entry.CompressedSize);`
|  | `1927` |  | `}`
|  | `1928` |  | ``
|  | `1929` |  | `if (entry.Offset >= 0xffffffff) {`
|  | `1930` |  | `ed.AddLeLong(entry.Offset);`
|  | `1931` |  | `}`
|  | `1932` |  | ``
|  | `1933` |  | `// Number of disk on which this file starts isnt supported and is never written here.`
|  | `1934` |  | `ed.AddNewEntry(1);`
|  | `1935` |  | `} else {`
|  | `1936` |  | `// Should have already be done when local header was added.`
|  | `1937` |  | `ed.Delete(1);`
|  | `1938` |  | `}`
|  | `1939` |  | ``
|  | `1940` |  | `byte[] centralExtraData = ed.GetEntryData();`
|  | `1941` |  | ``
|  | `1942` |  | `WriteLEShort(centralExtraData.Length);`
|  | `1943` |  | `WriteLEShort(entry.Comment != null ? entry.Comment.Length : 0);`
|  | `1944` |  | ``
|  | `1945` |  | `WriteLEShort(0); // disk number`
|  | `1946` |  | `WriteLEShort(0); // internal file attributes`
|  | `1947` |  | ``
|  | `1948` |  | `// External file attributes...`
|  | `1949` |  | `if (entry.ExternalFileAttributes != -1) {`
|  | `1950` |  | `WriteLEInt(entry.ExternalFileAttributes);`
|  | `1951` |  | `} else {`
|  | `1952` |  | `if (entry.IsDirectory) {`
|  | `1953` |  | `WriteLEUint(16);`
|  | `1954` |  | `} else {`
|  | `1955` |  | `WriteLEUint(0);`
|  | `1956` |  | `}`
|  | `1957` |  | `}`
|  | `1958` |  | ``
|  | `1959` |  | `if (entry.Offset >= 0xffffffff) {`
|  | `1960` |  | `WriteLEUint(0xffffffff);`
|  | `1961` |  | `} else {`
|  | `1962` |  | `WriteLEUint((uint)(int)entry.Offset);`
|  | `1963` |  | `}`
|  | `1964` |  | ``
|  | `1965` |  | `if (name.Length > 0) {`
|  | `1966` |  | `baseStream_.Write(name, 0, name.Length);`
|  | `1967` |  | `}`
|  | `1968` |  | ``
|  | `1969` |  | `if (centralExtraData.Length > 0) {`
|  | `1970` |  | `baseStream_.Write(centralExtraData, 0, centralExtraData.Length);`
|  | `1971` |  | `}`
|  | `1972` |  | ``
|  | `1973` |  | `byte[] rawComment = (entry.Comment != null) ? Encoding.ASCII.GetBytes(entry.Comment) : new byte[0];`
|  | `1974` |  | ``
|  | `1975` |  | `if (rawComment.Length > 0) {`
|  | `1976` |  | `baseStream_.Write(rawComment, 0, rawComment.Length);`
|  | `1977` |  | `}`
|  | `1978` |  | ``
|  | `1979` |  | `return ZipConstants.CentralHeaderBaseSize + name.Length + centralExtraData.Length + rawComment.Length;`
|  | `1980` |  | `}`
|  | `1981` |  | `#endregion`
|  | `1982` |  | ``
|  | `1983` |  | `void PostUpdateCleanup()`
|  | `1984` |  | `{`
|  | `1985` |  | `updateDataSource_ = null;`
|  | `1986` |  | `updates_ = null;`
|  | `1987` |  | `updateIndex_ = null;`
|  | `1988` |  | ``
|  | `1989` |  | `if (archiveStorage_ != null) {`
|  | `1990` |  | `archiveStorage_.Dispose();`
|  | `1991` |  | `archiveStorage_ = null;`
|  | `1992` |  | `}`
|  | `1993` |  | `}`
|  | `1994` |  | ``
|  | `1995` |  | `string GetTransformedFileName(string name)`
|  | `1996` |  | `{`
|  | `1997` |  | `INameTransform transform = NameTransform;`
|  | `1998` |  | `return (transform != null) ?`
|  | `1999` |  | `transform.TransformFile(name) :`
|  | `2000` |  | `name;`
|  | `2001` |  | `}`
|  | `2002` |  | ``
|  | `2003` |  | `string GetTransformedDirectoryName(string name)`
|  | `2004` |  | `{`
|  | `2005` |  | `INameTransform transform = NameTransform;`
|  | `2006` |  | `return (transform != null) ?`
|  | `2007` |  | `transform.TransformDirectory(name) :`
|  | `2008` |  | `name;`
|  | `2009` |  | `}`
|  | `2010` |  | ``
|  | `2011` |  | `/// <summary>`
|  | `2012` |  | `/// Get a raw memory buffer.`
|  | `2013` |  | `/// </summary>`
|  | `2014` |  | `/// <returns>Returns a raw memory buffer.</returns>`
|  | `2015` |  | `byte[] GetBuffer()`
|  | `2016` |  | `{`
|  | `2017` |  | `if (copyBuffer_ == null) {`
|  | `2018` |  | `copyBuffer_ = new byte[bufferSize_];`
|  | `2019` |  | `}`
|  | `2020` |  | `return copyBuffer_;`
|  | `2021` |  | `}`
|  | `2022` |  | ``
|  | `2023` |  | `void CopyDescriptorBytes(ZipUpdate update, Stream dest, Stream source)`
|  | `2024` |  | `{`
|  | `2025` |  | `int bytesToCopy = GetDescriptorSize(update);`
|  | `2026` |  | ``
|  | `2027` |  | `if (bytesToCopy > 0) {`
|  | `2028` |  | `byte[] buffer = GetBuffer();`
|  | `2029` |  | ``
|  | `2030` |  | `while (bytesToCopy > 0) {`
|  | `2031` |  | `int readSize = Math.Min(buffer.Length, bytesToCopy);`
|  | `2032` |  | ``
|  | `2033` |  | `int bytesRead = source.Read(buffer, 0, readSize);`
|  | `2034` |  | `if (bytesRead > 0) {`
|  | `2035` |  | `dest.Write(buffer, 0, bytesRead);`
|  | `2036` |  | `bytesToCopy -= bytesRead;`
|  | `2037` |  | `} else {`
|  | `2038` |  | `throw new ZipException("Unxpected end of stream");`
|  | `2039` |  | `}`
|  | `2040` |  | `}`
|  | `2041` |  | `}`
|  | `2042` |  | `}`
|  | `2043` |  | ``
|  | `2044` |  | `void CopyBytes(ZipUpdate update, Stream destination, Stream source,`
|  | `2045` |  | `long bytesToCopy, bool updateCrc)`
|  | `2046` |  | `{`
|  | `2047` |  | `if (destination == source) {`
|  | `2048` |  | `throw new InvalidOperationException("Destination and source are the same");`
|  | `2049` |  | `}`
|  | `2050` |  | ``
|  | `2051` |  | `// NOTE: Compressed size is updated elsewhere.`
|  | `2052` |  | `var crc = new Crc32();`
|  | `2053` |  | `byte[] buffer = GetBuffer();`
|  | `2054` |  | ``
|  | `2055` |  | `long targetBytes = bytesToCopy;`
|  | `2056` |  | `long totalBytesRead = 0;`
|  | `2057` |  | ``
|  | `2058` |  | `int bytesRead;`
|  | `2059` |  | `do {`
|  | `2060` |  | `int readSize = buffer.Length;`
|  | `2061` |  | ``
|  | `2062` |  | `if (bytesToCopy < readSize) {`
|  | `2063` |  | `readSize = (int)bytesToCopy;`
|  | `2064` |  | `}`
|  | `2065` |  | ``
|  | `2066` |  | `bytesRead = source.Read(buffer, 0, readSize);`
|  | `2067` |  | `if (bytesRead > 0) {`
|  | `2068` |  | `if (updateCrc) {`
|  | `2069` |  | `crc.Update(buffer, 0, bytesRead);`
|  | `2070` |  | `}`
|  | `2071` |  | `destination.Write(buffer, 0, bytesRead);`
|  | `2072` |  | `bytesToCopy -= bytesRead;`
|  | `2073` |  | `totalBytesRead += bytesRead;`
|  | `2074` |  | `}`
|  | `2075` |  | `}`
|  | `2076` |  | `while ((bytesRead > 0) && (bytesToCopy > 0));`
|  | `2077` |  | ``
|  | `2078` |  | `if (totalBytesRead != targetBytes) {`
|  | `2079` |  | `throw new ZipException(string.Format("Failed to copy bytes expected {0} read {1}", targetBytes, totalBytesRead))`
|  | `2080` |  | `}`
|  | `2081` |  | ``
|  | `2082` |  | `if (updateCrc) {`
|  | `2083` |  | `update.OutEntry.Crc = crc.Value;`
|  | `2084` |  | `}`
|  | `2085` |  | `}`
|  | `2086` |  | ``
|  | `2087` |  | `/// <summary>`
|  | `2088` |  | `/// Get the size of the source descriptor for a <see cref="ZipUpdate"/>.`
|  | `2089` |  | `/// </summary>`
|  | `2090` |  | `/// <param name="update">The update to get the size for.</param>`
|  | `2091` |  | `/// <returns>The descriptor size, zero if there isnt one.</returns>`
|  | `2092` |  | `int GetDescriptorSize(ZipUpdate update)`
|  | `2093` |  | `{`
|  | `2094` |  | `int result = 0;`
|  | `2095` |  | `if ((update.Entry.Flags & (int)GeneralBitFlags.Descriptor) != 0) {`
|  | `2096` |  | `result = ZipConstants.DataDescriptorSize - 4;`
|  | `2097` |  | `if (update.Entry.LocalHeaderRequiresZip64) {`
|  | `2098` |  | `result = ZipConstants.Zip64DataDescriptorSize - 4;`
|  | `2099` |  | `}`
|  | `2100` |  | `}`
|  | `2101` |  | `return result;`
|  | `2102` |  | `}`
|  | `2103` |  | ``
|  | `2104` |  | `void CopyDescriptorBytesDirect(ZipUpdate update, Stream stream, ref long destinationPosition, long sourcePosition)`
|  | `2105` |  | `{`
|  | `2106` |  | `int bytesToCopy = GetDescriptorSize(update);`
|  | `2107` |  | ``
|  | `2108` |  | `while (bytesToCopy > 0) {`
|  | `2109` |  | `var readSize = (int)bytesToCopy;`
|  | `2110` |  | `byte[] buffer = GetBuffer();`
|  | `2111` |  | ``
|  | `2112` |  | `stream.Position = sourcePosition;`
|  | `2113` |  | `int bytesRead = stream.Read(buffer, 0, readSize);`
|  | `2114` |  | `if (bytesRead > 0) {`
|  | `2115` |  | `stream.Position = destinationPosition;`
|  | `2116` |  | `stream.Write(buffer, 0, bytesRead);`
|  | `2117` |  | `bytesToCopy -= bytesRead;`
|  | `2118` |  | `destinationPosition += bytesRead;`
|  | `2119` |  | `sourcePosition += bytesRead;`
|  | `2120` |  | `} else {`
|  | `2121` |  | `throw new ZipException("Unxpected end of stream");`
|  | `2122` |  | `}`
|  | `2123` |  | `}`
|  | `2124` |  | `}`
|  | `2125` |  | ``
|  | `2126` |  | `void CopyEntryDataDirect(ZipUpdate update, Stream stream, bool updateCrc, ref long destinationPosition, ref long sou`
|  | `2127` |  | `{`
|  | `2128` |  | `long bytesToCopy = update.Entry.CompressedSize;`
|  | `2129` |  | ``
|  | `2130` |  | `// NOTE: Compressed size is updated elsewhere.`
|  | `2131` |  | `var crc = new Crc32();`
|  | `2132` |  | `byte[] buffer = GetBuffer();`
|  | `2133` |  | ``
|  | `2134` |  | `long targetBytes = bytesToCopy;`
|  | `2135` |  | `long totalBytesRead = 0;`
|  | `2136` |  | ``
|  | `2137` |  | `int bytesRead;`
|  | `2138` |  | `do {`
|  | `2139` |  | `int readSize = buffer.Length;`
|  | `2140` |  | ``
|  | `2141` |  | `if (bytesToCopy < readSize) {`
|  | `2142` |  | `readSize = (int)bytesToCopy;`
|  | `2143` |  | `}`
|  | `2144` |  | ``
|  | `2145` |  | `stream.Position = sourcePosition;`
|  | `2146` |  | `bytesRead = stream.Read(buffer, 0, readSize);`
|  | `2147` |  | `if (bytesRead > 0) {`
|  | `2148` |  | `if (updateCrc) {`
|  | `2149` |  | `crc.Update(buffer, 0, bytesRead);`
|  | `2150` |  | `}`
|  | `2151` |  | `stream.Position = destinationPosition;`
|  | `2152` |  | `stream.Write(buffer, 0, bytesRead);`
|  | `2153` |  | ``
|  | `2154` |  | `destinationPosition += bytesRead;`
|  | `2155` |  | `sourcePosition += bytesRead;`
|  | `2156` |  | `bytesToCopy -= bytesRead;`
|  | `2157` |  | `totalBytesRead += bytesRead;`
|  | `2158` |  | `}`
|  | `2159` |  | `}`
|  | `2160` |  | `while ((bytesRead > 0) && (bytesToCopy > 0));`
|  | `2161` |  | ``
|  | `2162` |  | `if (totalBytesRead != targetBytes) {`
|  | `2163` |  | `throw new ZipException(string.Format("Failed to copy bytes expected {0} read {1}", targetBytes, totalBytesRead))`
|  | `2164` |  | `}`
|  | `2165` |  | ``
|  | `2166` |  | `if (updateCrc) {`
|  | `2167` |  | `update.OutEntry.Crc = crc.Value;`
|  | `2168` |  | `}`
|  | `2169` |  | `}`
|  | `2170` |  | ``
|  | `2171` |  | `int FindExistingUpdate(ZipEntry entry)`
|  | `2172` |  | `{`
|  | `2173` |  | `int result = -1;`
|  | `2174` |  | `string convertedName = GetTransformedFileName(entry.Name);`
|  | `2175` |  | ``
|  | `2176` |  | `if (updateIndex_.ContainsKey(convertedName)) {`
|  | `2177` |  | `result = (int)updateIndex_[convertedName];`
|  | `2178` |  | `}`
|  | `2179` |  | `/*`
|  | `2180` |  | `// This is slow like the coming of the next ice age but takes less storage and may be useful`
|  | `2181` |  | `// for CF?`
|  | `2182` |  | `for (int index = 0; index < updates_.Count; ++index)`
|  | `2183` |  | `{`
|  | `2184` |  | `ZipUpdate zu = ( ZipUpdate )updates_[index];`
|  | `2185` |  | `if ( (zu.Entry.ZipFileIndex == entry.ZipFileIndex) &&`
|  | `2186` |  | `(string.Compare(convertedName, zu.Entry.Name, true, CultureInfo.InvariantCulture) == 0) ) {`
|  | `2187` |  | `result = index;`
|  | `2188` |  | `break;`
|  | `2189` |  | `}`
|  | `2190` |  | `}`
|  | `2191` |  | `*/`
|  | `2192` |  | `return result;`
|  | `2193` |  | `}`
|  | `2194` |  | ``
|  | `2195` |  | `int FindExistingUpdate(string fileName)`
|  | `2196` |  | `{`
|  | `2197` |  | `int result = -1;`
|  | `2198` |  | ``
|  | `2199` |  | `string convertedName = GetTransformedFileName(fileName);`
|  | `2200` |  | ``
|  | `2201` |  | `if (updateIndex_.ContainsKey(convertedName)) {`
|  | `2202` |  | `result = (int)updateIndex_[convertedName];`
|  | `2203` |  | `}`
|  | `2204` |  | ``
|  | `2205` |  | `/*`
|  | `2206` |  | `// This is slow like the coming of the next ice age but takes less storage and may be useful`
|  | `2207` |  | `// for CF?`
|  | `2208` |  | `for ( int index = 0; index < updates_.Count; ++index ) {`
|  | `2209` |  | `if ( string.Compare(convertedName, (( ZipUpdate )updates_[index]).Entry.Name,`
|  | `2210` |  | `true, CultureInfo.InvariantCulture) == 0 ) {`
|  | `2211` |  | `result = index;`
|  | `2212` |  | `break;`
|  | `2213` |  | `}`
|  | `2214` |  | `}`
|  | `2215` |  | `*/`
|  | `2216` |  | ``
|  | `2217` |  | `return result;`
|  | `2218` |  | `}`
|  | `2219` |  | ``
|  | `2220` |  | `/// <summary>`
|  | `2221` |  | `/// Get an output stream for the specified <see cref="ZipEntry"/>`
|  | `2222` |  | `/// </summary>`
|  | `2223` |  | `/// <param name="entry">The entry to get an output stream for.</param>`
|  | `2224` |  | `/// <returns>The output stream obtained for the entry.</returns>`
|  | `2225` |  | `Stream GetOutputStream(ZipEntry entry)`
|  | `2226` |  | `{`
|  | `2227` |  | `Stream result = baseStream_;`
|  | `2228` |  | ``
|  | `2229` |  | `if (entry.IsCrypted == true) {`
|  | `2230` |  | `result = CreateAndInitEncryptionStream(result, entry);`
|  | `2231` |  | `}`
|  | `2232` |  | ``
|  | `2233` |  | `switch (entry.CompressionMethod) {`
|  | `2234` |  | `case CompressionMethod.Stored:`
|  | `2235` |  | `result = new UncompressedStream(result);`
|  | `2236` |  | `break;`
|  | `2237` |  | ``
|  | `2238` |  | `case CompressionMethod.Deflated:`
|  | `2239` |  | `var dos = new DeflaterOutputStream(result, new Deflater(9, true));`
|  | `2240` |  | `dos.IsStreamOwner = false;`
|  | `2241` |  | `result = dos;`
|  | `2242` |  | `break;`
|  | `2243` |  | ``
|  | `2244` |  | `default:`
|  | `2245` |  | `throw new ZipException("Unknown compression method " + entry.CompressionMethod);`
|  | `2246` |  | `}`
|  | `2247` |  | `return result;`
|  | `2248` |  | `}`
|  | `2249` |  | ``
|  | `2250` |  | `void AddEntry(ZipFile workFile, ZipUpdate update)`
|  | `2251` |  | `{`
|  | `2252` |  | `Stream source = null;`
|  | `2253` |  | ``
|  | `2254` |  | `if (update.Entry.IsFile) {`
|  | `2255` |  | `source = update.GetSource();`
|  | `2256` |  | ``
|  | `2257` |  | `if (source == null) {`
|  | `2258` |  | `source = updateDataSource_.GetSource(update.Entry, update.Filename);`
|  | `2259` |  | `}`
|  | `2260` |  | `}`
|  | `2261` |  | ``
|  | `2262` |  | `if (source != null) {`
|  | `2263` |  | `using (source) {`
|  | `2264` |  | `long sourceStreamLength = source.Length;`
|  | `2265` |  | `if (update.OutEntry.Size < 0) {`
|  | `2266` |  | `update.OutEntry.Size = sourceStreamLength;`
|  | `2267` |  | `} else {`
|  | `2268` |  | `// Check for errant entries.`
|  | `2269` |  | `if (update.OutEntry.Size != sourceStreamLength) {`
|  | `2270` |  | `throw new ZipException("Entry size/stream size mismatch");`
|  | `2271` |  | `}`
|  | `2272` |  | `}`
|  | `2273` |  | ``
|  | `2274` |  | `workFile.WriteLocalEntryHeader(update);`
|  | `2275` |  | ``
|  | `2276` |  | `long dataStart = workFile.baseStream_.Position;`
|  | `2277` |  | ``
|  | `2278` |  | `using (Stream output = workFile.GetOutputStream(update.OutEntry)) {`
|  | `2279` |  | `CopyBytes(update, output, source, sourceStreamLength, true);`
|  | `2280` |  | `}`
|  | `2281` |  | ``
|  | `2282` |  | `long dataEnd = workFile.baseStream_.Position;`
|  | `2283` |  | `update.OutEntry.CompressedSize = dataEnd - dataStart;`
|  | `2284` |  | ``
|  | `2285` |  | `if ((update.OutEntry.Flags & (int)GeneralBitFlags.Descriptor) == (int)GeneralBitFlags.Descriptor) {`
|  | `2286` |  | `var helper = new ZipHelperStream(workFile.baseStream_);`
|  | `2287` |  | `helper.WriteDataDescriptor(update.OutEntry);`
|  | `2288` |  | `}`
|  | `2289` |  | `}`
|  | `2290` |  | `} else {`
|  | `2291` |  | `workFile.WriteLocalEntryHeader(update);`
|  | `2292` |  | `update.OutEntry.CompressedSize = 0;`
|  | `2293` |  | `}`
|  | `2294` |  | ``
|  | `2295` |  | `}`
|  | `2296` |  | ``
|  | `2297` |  | `void ModifyEntry(ZipFile workFile, ZipUpdate update)`
|  | `2298` |  | `{`
|  | `2299` |  | `workFile.WriteLocalEntryHeader(update);`
|  | `2300` |  | `long dataStart = workFile.baseStream_.Position;`
|  | `2301` |  | ``
|  | `2302` |  | `// TODO: This is slow if the changes don't effect the data!!`
|  | `2303` |  | `if (update.Entry.IsFile && (update.Filename != null)) {`
|  | `2304` |  | `using (Stream output = workFile.GetOutputStream(update.OutEntry)) {`
|  | `2305` |  | `using (Stream source = this.GetInputStream(update.Entry)) {`
|  | `2306` |  | `CopyBytes(update, output, source, source.Length, true);`
|  | `2307` |  | `}`
|  | `2308` |  | `}`
|  | `2309` |  | `}`
|  | `2310` |  | ``
|  | `2311` |  | `long dataEnd = workFile.baseStream_.Position;`
|  | `2312` |  | `update.Entry.CompressedSize = dataEnd - dataStart;`
|  | `2313` |  | `}`
|  | `2314` |  | ``
|  | `2315` |  | `void CopyEntryDirect(ZipFile workFile, ZipUpdate update, ref long destinationPosition)`
|  | `2316` |  | `{`
|  | `2317` |  | `bool skipOver = false || update.Entry.Offset == destinationPosition;`
|  | `2318` |  | ``
|  | `2319` |  | `if (!skipOver) {`
|  | `2320` |  | `baseStream_.Position = destinationPosition;`
|  | `2321` |  | `workFile.WriteLocalEntryHeader(update);`
|  | `2322` |  | `destinationPosition = baseStream_.Position;`
|  | `2323` |  | `}`
|  | `2324` |  | ``
|  | `2325` |  | `long sourcePosition = 0;`
|  | `2326` |  | ``
|  | `2327` |  | `const int NameLengthOffset = 26;`
|  | `2328` |  | ``
|  | `2329` |  | `// TODO: Add base for SFX friendly handling`
|  | `2330` |  | `long entryDataOffset = update.Entry.Offset + NameLengthOffset;`
|  | `2331` |  | ``
|  | `2332` |  | `baseStream_.Seek(entryDataOffset, SeekOrigin.Begin);`
|  | `2333` |  | ``
|  | `2334` |  | `// Clumsy way of handling retrieving the original name and extra data length for now.`
|  | `2335` |  | `// TODO: Stop re-reading name and data length in CopyEntryDirect.`
|  | `2336` |  | `uint nameLength = ReadLEUshort();`
|  | `2337` |  | `uint extraLength = ReadLEUshort();`
|  | `2338` |  | ``
|  | `2339` |  | `sourcePosition = baseStream_.Position + nameLength + extraLength;`
|  | `2340` |  | ``
|  | `2341` |  | `if (skipOver) {`
|  | `2342` |  | `if (update.OffsetBasedSize != -1)`
|  | `2343` |  | `destinationPosition += update.OffsetBasedSize;`
|  | `2344` |  | `else`
|  | `2345` |  | `// TODO: Find out why this calculation comes up 4 bytes short on some entries in ODT (Office Document Text) ar`
|  | `2346` |  | `// WinZip produces a warning on these entries:`
|  | `2347` |  | `// "caution: value of lrec.csize (compressed size) changed from ..."`
|  | `2348` |  | `destinationPosition +=`
|  | `2349` |  | `(sourcePosition - entryDataOffset) + NameLengthOffset + // Header size`
|  | `2350` |  | `update.Entry.CompressedSize + GetDescriptorSize(update);`
|  | `2351` |  | `} else {`
|  | `2352` |  | `if (update.Entry.CompressedSize > 0) {`
|  | `2353` |  | `CopyEntryDataDirect(update, baseStream_, false, ref destinationPosition, ref sourcePosition);`
|  | `2354` |  | `}`
|  | `2355` |  | `CopyDescriptorBytesDirect(update, baseStream_, ref destinationPosition, sourcePosition);`
|  | `2356` |  | `}`
|  | `2357` |  | `}`
|  | `2358` |  | ``
|  | `2359` |  | `void CopyEntry(ZipFile workFile, ZipUpdate update)`
|  | `2360` |  | `{`
|  | `2361` |  | `workFile.WriteLocalEntryHeader(update);`
|  | `2362` |  | ``
|  | `2363` |  | `if (update.Entry.CompressedSize > 0) {`
|  | `2364` |  | `const int NameLengthOffset = 26;`
|  | `2365` |  | ``
|  | `2366` |  | `long entryDataOffset = update.Entry.Offset + NameLengthOffset;`
|  | `2367` |  | ``
|  | `2368` |  | `// TODO: This wont work for SFX files!`
|  | `2369` |  | `baseStream_.Seek(entryDataOffset, SeekOrigin.Begin);`
|  | `2370` |  | ``
|  | `2371` |  | `uint nameLength = ReadLEUshort();`
|  | `2372` |  | `uint extraLength = ReadLEUshort();`
|  | `2373` |  | ``
|  | `2374` |  | `baseStream_.Seek(nameLength + extraLength, SeekOrigin.Current);`
|  | `2375` |  | ``
|  | `2376` |  | `CopyBytes(update, workFile.baseStream_, baseStream_, update.Entry.CompressedSize, false);`
|  | `2377` |  | `}`
|  | `2378` |  | `CopyDescriptorBytes(update, workFile.baseStream_, baseStream_);`
|  | `2379` |  | `}`
|  | `2380` |  | ``
|  | `2381` |  | `void Reopen(Stream source)`
|  | `2382` |  | `{`
|  | `2383` |  | `if (source == null) {`
|  | `2384` |  | `throw new ZipException("Failed to reopen archive - no source");`
|  | `2385` |  | `}`
|  | `2386` |  | ``
|  | `2387` |  | `isNewArchive_ = false;`
|  | `2388` |  | `baseStream_ = source;`
|  | `2389` |  | `ReadEntries();`
|  | `2390` |  | `}`
|  | `2391` |  | ``
|  | `2392` |  | `void Reopen()`
|  | `2393` |  | `{`
|  | `2394` |  | `if (Name == null) {`
|  | `2395` |  | `throw new InvalidOperationException("Name is not known cannot Reopen");`
|  | `2396` |  | `}`
|  | `2397` |  | ``
|  | `2398` |  | `Reopen(File.Open(Name, FileMode.Open, FileAccess.Read, FileShare.Read));`
|  | `2399` |  | `}`
|  | `2400` |  | ``
|  | `2401` |  | `void UpdateCommentOnly()`
|  | `2402` |  | `{`
|  | `2403` |  | `long baseLength = baseStream_.Length;`
|  | `2404` |  | ``
|  | `2405` |  | `ZipHelperStream updateFile = null;`
|  | `2406` |  | ``
|  | `2407` |  | `if (archiveStorage_.UpdateMode == FileUpdateMode.Safe) {`
|  | `2408` |  | `Stream copyStream = archiveStorage_.MakeTemporaryCopy(baseStream_);`
|  | `2409` |  | `updateFile = new ZipHelperStream(copyStream);`
|  | `2410` |  | `updateFile.IsStreamOwner = true;`
|  | `2411` |  | ``
|  | `2412` |  | `baseStream_.Close();`
|  | `2413` |  | `baseStream_ = null;`
|  | `2414` |  | `} else {`
|  | `2415` |  | `if (archiveStorage_.UpdateMode == FileUpdateMode.Direct) {`
|  | `2416` |  | `// TODO: archiveStorage wasnt originally intended for this use.`
|  | `2417` |  | `// Need to revisit this to tidy up handling as archive storage currently doesnt`
|  | `2418` |  | `// handle the original stream well.`
|  | `2419` |  | `// The problem is when using an existing zip archive with an in memory archive storage.`
|  | `2420` |  | `// The open stream wont support writing but the memory storage should open the same file not an in memory one.`
|  | `2421` |  | ``
|  | `2422` |  | `// Need to tidy up the archive storage interface and contract basically.`
|  | `2423` |  | `baseStream_ = archiveStorage_.OpenForDirectUpdate(baseStream_);`
|  | `2424` |  | `updateFile = new ZipHelperStream(baseStream_);`
|  | `2425` |  | `} else {`
|  | `2426` |  | `baseStream_.Close();`
|  | `2427` |  | `baseStream_ = null;`
|  | `2428` |  | `updateFile = new ZipHelperStream(Name);`
|  | `2429` |  | `}`
|  | `2430` |  | `}`
|  | `2431` |  | ``
|  | `2432` |  | `using (updateFile) {`
|  | `2433` |  | `long locatedCentralDirOffset =`
|  | `2434` |  | `updateFile.LocateBlockWithSignature(ZipConstants.EndOfCentralDirectorySignature,`
|  | `2435` |  | `baseLength, ZipConstants.EndOfCentralRecordBaseSize, 0xffff);`
|  | `2436` |  | `if (locatedCentralDirOffset < 0) {`
|  | `2437` |  | `throw new ZipException("Cannot find central directory");`
|  | `2438` |  | `}`
|  | `2439` |  | ``
|  | `2440` |  | `const int CentralHeaderCommentSizeOffset = 16;`
|  | `2441` |  | `updateFile.Position += CentralHeaderCommentSizeOffset;`
|  | `2442` |  | ``
|  | `2443` |  | `byte[] rawComment = newComment_.RawComment;`
|  | `2444` |  | ``
|  | `2445` |  | `updateFile.WriteLEShort(rawComment.Length);`
|  | `2446` |  | `updateFile.Write(rawComment, 0, rawComment.Length);`
|  | `2447` |  | `updateFile.SetLength(updateFile.Position);`
|  | `2448` |  | `}`
|  | `2449` |  | ``
|  | `2450` |  | `if (archiveStorage_.UpdateMode == FileUpdateMode.Safe) {`
|  | `2451` |  | `Reopen(archiveStorage_.ConvertTemporaryToFinal());`
|  | `2452` |  | `} else {`
|  | `2453` |  | `ReadEntries();`
|  | `2454` |  | `}`
|  | `2455` |  | `}`
|  | `2456` |  | ``
|  | `2457` |  | `/// <summary>`
|  | `2458` |  | `/// Class used to sort updates.`
|  | `2459` |  | `/// </summary>`
|  | `2460` |  | `class UpdateComparer : IComparer`
|  | `2461` |  | `{`
|  | `2462` |  | `/// <summary>`
|  | `2463` |  | `/// Compares two objects and returns a value indicating whether one is`
|  | `2464` |  | `/// less than, equal to or greater than the other.`
|  | `2465` |  | `/// </summary>`
|  | `2466` |  | `/// <param name="x">First object to compare</param>`
|  | `2467` |  | `/// <param name="y">Second object to compare.</param>`
|  | `2468` |  | `/// <returns>Compare result.</returns>`
|  | `2469` |  | `public int Compare(`
|  | `2470` |  | `object x,`
|  | `2471` |  | `object y)`
|  | `2472` |  | `{`
|  | `2473` |  | `var zx = x as ZipUpdate;`
|  | `2474` |  | `var zy = y as ZipUpdate;`
|  | `2475` |  | ``
|  | `2476` |  | `int result;`
|  | `2477` |  | ``
|  | `2478` |  | `if (zx == null) {`
|  | `2479` |  | `if (zy == null) {`
|  | `2480` |  | `result = 0;`
|  | `2481` |  | `} else {`
|  | `2482` |  | `result = -1;`
|  | `2483` |  | `}`
|  | `2484` |  | `} else if (zy == null) {`
|  | `2485` |  | `result = 1;`
|  | `2486` |  | `} else {`
|  | `2487` |  | `int xCmdValue = ((zx.Command == UpdateCommand.Copy) || (zx.Command == UpdateCommand.Modify)) ? 0 : 1;`
|  | `2488` |  | `int yCmdValue = ((zy.Command == UpdateCommand.Copy) || (zy.Command == UpdateCommand.Modify)) ? 0 : 1;`
|  | `2489` |  | ``
|  | `2490` |  | `result = xCmdValue - yCmdValue;`
|  | `2491` |  | `if (result == 0) {`
|  | `2492` |  | `long offsetDiff = zx.Entry.Offset - zy.Entry.Offset;`
|  | `2493` |  | `if (offsetDiff < 0) {`
|  | `2494` |  | `result = -1;`
|  | `2495` |  | `} else if (offsetDiff == 0) {`
|  | `2496` |  | `result = 0;`
|  | `2497` |  | `} else {`
|  | `2498` |  | `result = 1;`
|  | `2499` |  | `}`
|  | `2500` |  | `}`
|  | `2501` |  | `}`
|  | `2502` |  | `return result;`
|  | `2503` |  | `}`
|  | `2504` |  | `}`
|  | `2505` |  | ``
|  | `2506` |  | `void RunUpdates()`
|  | `2507` |  | `{`
|  | `2508` |  | `long sizeEntries = 0;`
|  | `2509` |  | `long endOfStream = 0;`
|  | `2510` |  | `bool directUpdate = false;`
|  | `2511` |  | `long destinationPosition = 0; // NOT SFX friendly`
|  | `2512` |  | ``
|  | `2513` |  | `ZipFile workFile;`
|  | `2514` |  | ``
|  | `2515` |  | `if (IsNewArchive) {`
|  | `2516` |  | `workFile = this;`
|  | `2517` |  | `workFile.baseStream_.Position = 0;`
|  | `2518` |  | `directUpdate = true;`
|  | `2519` |  | `} else if (archiveStorage_.UpdateMode == FileUpdateMode.Direct) {`
|  | `2520` |  | `workFile = this;`
|  | `2521` |  | `workFile.baseStream_.Position = 0;`
|  | `2522` |  | `directUpdate = true;`
|  | `2523` |  | ``
|  | `2524` |  | `// Sort the updates by offset within copies/modifies, then adds.`
|  | `2525` |  | `// This ensures that data required by copies will not be overwritten.`
|  | `2526` |  | `updates_.Sort(new UpdateComparer());`
|  | `2527` |  | `} else {`
|  | `2528` |  | `workFile = ZipFile.Create(archiveStorage_.GetTemporaryOutput());`
|  | `2529` |  | `workFile.UseZip64 = UseZip64;`
|  | `2530` |  | ``
|  | `2531` |  | `if (key != null) {`
|  | `2532` |  | `workFile.key = (byte[])key.Clone();`
|  | `2533` |  | `}`
|  | `2534` |  | `}`
|  | `2535` |  | ``
|  | `2536` |  | `try {`
|  | `2537` |  | `foreach (ZipUpdate update in updates_) {`
|  | `2538` |  | `if (update != null) {`
|  | `2539` |  | `switch (update.Command) {`
|  | `2540` |  | `case UpdateCommand.Copy:`
|  | `2541` |  | `if (directUpdate) {`
|  | `2542` |  | `CopyEntryDirect(workFile, update, ref destinationPosition);`
|  | `2543` |  | `} else {`
|  | `2544` |  | `CopyEntry(workFile, update);`
|  | `2545` |  | `}`
|  | `2546` |  | `break;`
|  | `2547` |  | ``
|  | `2548` |  | `case UpdateCommand.Modify:`
|  | `2549` |  | `// TODO: Direct modifying of an entry will take some legwork.`
|  | `2550` |  | `ModifyEntry(workFile, update);`
|  | `2551` |  | `break;`
|  | `2552` |  | ``
|  | `2553` |  | `case UpdateCommand.Add:`
|  | `2554` |  | `if (!IsNewArchive && directUpdate) {`
|  | `2555` |  | `workFile.baseStream_.Position = destinationPosition;`
|  | `2556` |  | `}`
|  | `2557` |  | ``
|  | `2558` |  | `AddEntry(workFile, update);`
|  | `2559` |  | ``
|  | `2560` |  | `if (directUpdate) {`
|  | `2561` |  | `destinationPosition = workFile.baseStream_.Position;`
|  | `2562` |  | `}`
|  | `2563` |  | `break;`
|  | `2564` |  | `}`
|  | `2565` |  | `}`
|  | `2566` |  | `}`
|  | `2567` |  | ``
|  | `2568` |  | `if (!IsNewArchive && directUpdate) {`
|  | `2569` |  | `workFile.baseStream_.Position = destinationPosition;`
|  | `2570` |  | `}`
|  | `2571` |  | ``
|  | `2572` |  | `long centralDirOffset = workFile.baseStream_.Position;`
|  | `2573` |  | ``
|  | `2574` |  | `foreach (ZipUpdate update in updates_) {`
|  | `2575` |  | `if (update != null) {`
|  | `2576` |  | `sizeEntries += workFile.WriteCentralDirectoryHeader(update.OutEntry);`
|  | `2577` |  | `}`
|  | `2578` |  | `}`
|  | `2579` |  | ``
|  | `2580` |  | `byte[] theComment = (newComment_ != null) ? newComment_.RawComment : ZipConstants.ConvertToArray(comment_);`
|  | `2581` |  | `using (ZipHelperStream zhs = new ZipHelperStream(workFile.baseStream_)) {`
|  | `2582` |  | `zhs.WriteEndOfCentralDirectory(updateCount_, sizeEntries, centralDirOffset, theComment);`
|  | `2583` |  | `}`
|  | `2584` |  | ``
|  | `2585` |  | `endOfStream = workFile.baseStream_.Position;`
|  | `2586` |  | ``
|  | `2587` |  | `// And now patch entries...`
|  | `2588` |  | `foreach (ZipUpdate update in updates_) {`
|  | `2589` |  | `if (update != null) {`
|  | `2590` |  | `// If the size of the entry is zero leave the crc as 0 as well.`
|  | `2591` |  | `// The calculated crc will be all bits on...`
|  | `2592` |  | `if ((update.CrcPatchOffset > 0) && (update.OutEntry.CompressedSize > 0)) {`
|  | `2593` |  | `workFile.baseStream_.Position = update.CrcPatchOffset;`
|  | `2594` |  | `workFile.WriteLEInt((int)update.OutEntry.Crc);`
|  | `2595` |  | `}`
|  | `2596` |  | ``
|  | `2597` |  | `if (update.SizePatchOffset > 0) {`
|  | `2598` |  | `workFile.baseStream_.Position = update.SizePatchOffset;`
|  | `2599` |  | `if (update.OutEntry.LocalHeaderRequiresZip64) {`
|  | `2600` |  | `workFile.WriteLeLong(update.OutEntry.Size);`
|  | `2601` |  | `workFile.WriteLeLong(update.OutEntry.CompressedSize);`
|  | `2602` |  | `} else {`
|  | `2603` |  | `workFile.WriteLEInt((int)update.OutEntry.CompressedSize);`
|  | `2604` |  | `workFile.WriteLEInt((int)update.OutEntry.Size);`
|  | `2605` |  | `}`
|  | `2606` |  | `}`
|  | `2607` |  | `}`
|  | `2608` |  | `}`
|  | `2609` |  | `} catch {`
|  | `2610` |  | `workFile.Close();`
|  | `2611` |  | `if (!directUpdate && (workFile.Name != null)) {`
|  | `2612` |  | `File.Delete(workFile.Name);`
|  | `2613` |  | `}`
|  | `2614` |  | `throw;`
|  | `2615` |  | `}`
|  | `2616` |  | ``
|  | `2617` |  | `if (directUpdate) {`
|  | `2618` |  | `workFile.baseStream_.SetLength(endOfStream);`
|  | `2619` |  | `workFile.baseStream_.Flush();`
|  | `2620` |  | `isNewArchive_ = false;`
|  | `2621` |  | `ReadEntries();`
|  | `2622` |  | `} else {`
|  | `2623` |  | `baseStream_.Close();`
|  | `2624` |  | `Reopen(archiveStorage_.ConvertTemporaryToFinal());`
|  | `2625` |  | `}`
|  | `2626` |  | `}`
|  | `2627` |  | ``
|  | `2628` |  | `void CheckUpdating()`
|  | `2629` |  | `{`
|  | `2630` |  | `if (updates_ == null) {`
|  | `2631` |  | `throw new InvalidOperationException("BeginUpdate has not been called");`
|  | `2632` |  | `}`
|  | `2633` |  | `}`
|  | `2634` |  | ``
|  | `2635` |  | `#endregion`
|  | `2636` |  | ``
|  | `2637` |  | `#region ZipUpdate class`
|  | `2638` |  | `/// <summary>`
|  | `2639` |  | `/// Represents a pending update to a Zip file.`
|  | `2640` |  | `/// </summary>`
|  | `2641` |  | `class ZipUpdate`
|  | `2642` |  | `{`
|  | `2643` |  | `#region Constructors`
|  | `2644` |  | `public ZipUpdate(string fileName, ZipEntry entry)`
|  | `2645` |  | `{`
|  | `2646` |  | `command_ = UpdateCommand.Add;`
|  | `2647` |  | `entry_ = entry;`
|  | `2648` |  | `filename_ = fileName;`
|  | `2649` |  | `}`
|  | `2650` |  | ``
|  | `2651` |  | `[Obsolete]`
|  | `2652` |  | `public ZipUpdate(string fileName, string entryName, CompressionMethod compressionMethod)`
|  | `2653` |  | `{`
|  | `2654` |  | `command_ = UpdateCommand.Add;`
|  | `2655` |  | `entry_ = new ZipEntry(entryName);`
|  | `2656` |  | `entry_.CompressionMethod = compressionMethod;`
|  | `2657` |  | `filename_ = fileName;`
|  | `2658` |  | `}`
|  | `2659` |  | ``
|  | `2660` |  | `[Obsolete]`
|  | `2661` |  | `public ZipUpdate(string fileName, string entryName)`
|  | `2662` |  | `: this(fileName, entryName, CompressionMethod.Deflated)`
|  | `2663` |  | `{`
|  | `2664` |  | `// Do nothing.`
|  | `2665` |  | `}`
|  | `2666` |  | ``
|  | `2667` |  | `[Obsolete]`
|  | `2668` |  | `public ZipUpdate(IStaticDataSource dataSource, string entryName, CompressionMethod compressionMethod)`
|  | `2669` |  | `{`
|  | `2670` |  | `command_ = UpdateCommand.Add;`
|  | `2671` |  | `entry_ = new ZipEntry(entryName);`
|  | `2672` |  | `entry_.CompressionMethod = compressionMethod;`
|  | `2673` |  | `dataSource_ = dataSource;`
|  | `2674` |  | `}`
|  | `2675` |  | ``
|  | `2676` |  | `public ZipUpdate(IStaticDataSource dataSource, ZipEntry entry)`
|  | `2677` |  | `{`
|  | `2678` |  | `command_ = UpdateCommand.Add;`
|  | `2679` |  | `entry_ = entry;`
|  | `2680` |  | `dataSource_ = dataSource;`
|  | `2681` |  | `}`
|  | `2682` |  | ``
|  | `2683` |  | `public ZipUpdate(ZipEntry original, ZipEntry updated)`
|  | `2684` |  | `{`
|  | `2685` |  | `throw new ZipException("Modify not currently supported");`
|  | `2686` |  | `/*`
|  | `2687` |  | `command_ = UpdateCommand.Modify;`
|  | `2688` |  | `entry_ = ( ZipEntry )original.Clone();`
|  | `2689` |  | `outEntry_ = ( ZipEntry )updated.Clone();`
|  | `2690` |  | `*/`
|  | `2691` |  | `}`
|  | `2692` |  | ``
|  | `2693` |  | `public ZipUpdate(UpdateCommand command, ZipEntry entry)`
|  | `2694` |  | `{`
|  | `2695` |  | `command_ = command;`
|  | `2696` |  | `entry_ = (ZipEntry)entry.Clone();`
|  | `2697` |  | `}`
|  | `2698` |  | ``
|  | `2699` |  | ``
|  | `2700` |  | `/// <summary>`
|  | `2701` |  | `/// Copy an existing entry.`
|  | `2702` |  | `/// </summary>`
|  | `2703` |  | `/// <param name="entry">The existing entry to copy.</param>`
|  | `2704` |  | `public ZipUpdate(ZipEntry entry)`
|  | `2705` |  | `: this(UpdateCommand.Copy, entry)`
|  | `2706` |  | `{`
|  | `2707` |  | `// Do nothing.`
|  | `2708` |  | `}`
|  | `2709` |  | `#endregion`
|  | `2710` |  | ``
|  | `2711` |  | `/// <summary>`
|  | `2712` |  | `/// Get the <see cref="ZipEntry"/> for this update.`
|  | `2713` |  | `/// </summary>`
|  | `2714` |  | `/// <remarks>This is the source or original entry.</remarks>`
|  | `2715` |  | `public ZipEntry Entry {`
|  | `2716` |  | `get { return entry_; }`
|  | `2717` |  | `}`
|  | `2718` |  | ``
|  | `2719` |  | `/// <summary>`
|  | `2720` |  | `/// Get the <see cref="ZipEntry"/> that will be written to the updated/new file.`
|  | `2721` |  | `/// </summary>`
|  | `2722` |  | `public ZipEntry OutEntry {`
|  | `2723` |  | `get {`
|  | `2724` |  | `if (outEntry_ == null) {`
|  | `2725` |  | `outEntry_ = (ZipEntry)entry_.Clone();`
|  | `2726` |  | `}`
|  | `2727` |  | ``
|  | `2728` |  | `return outEntry_;`
|  | `2729` |  | `}`
|  | `2730` |  | `}`
|  | `2731` |  | ``
|  | `2732` |  | `/// <summary>`
|  | `2733` |  | `/// Get the command for this update.`
|  | `2734` |  | `/// </summary>`
|  | `2735` |  | `public UpdateCommand Command {`
|  | `2736` |  | `get { return command_; }`
|  | `2737` |  | `}`
|  | `2738` |  | ``
|  | `2739` |  | `/// <summary>`
|  | `2740` |  | `/// Get the filename if any for this update. Null if none exists.`
|  | `2741` |  | `/// </summary>`
|  | `2742` |  | `public string Filename {`
|  | `2743` |  | `get { return filename_; }`
|  | `2744` |  | `}`
|  | `2745` |  | ``
|  | `2746` |  | `/// <summary>`
|  | `2747` |  | `/// Get/set the location of the size patch for this update.`
|  | `2748` |  | `/// </summary>`
|  | `2749` |  | `public long SizePatchOffset {`
|  | `2750` |  | `get { return sizePatchOffset_; }`
|  | `2751` |  | `set { sizePatchOffset_ = value; }`
|  | `2752` |  | `}`
|  | `2753` |  | ``
|  | `2754` |  | `/// <summary>`
|  | `2755` |  | `/// Get /set the location of the crc patch for this update.`
|  | `2756` |  | `/// </summary>`
|  | `2757` |  | `public long CrcPatchOffset {`
|  | `2758` |  | `get { return crcPatchOffset_; }`
|  | `2759` |  | `set { crcPatchOffset_ = value; }`
|  | `2760` |  | `}`
|  | `2761` |  | ``
|  | `2762` |  | `/// <summary>`
|  | `2763` |  | `/// Get/set the size calculated by offset.`
|  | `2764` |  | `/// Specifically, the difference between this and next entry's starting offset.`
|  | `2765` |  | `/// </summary>`
|  | `2766` |  | `public long OffsetBasedSize {`
|  | `2767` |  | `get { return _offsetBasedSize; }`
|  | `2768` |  | `set { _offsetBasedSize = value; }`
|  | `2769` |  | `}`
|  | `2770` |  | ``
|  | `2771` |  | `public Stream GetSource()`
|  | `2772` |  | `{`
|  | `2773` |  | `Stream result = null;`
|  | `2774` |  | `if (dataSource_ != null) {`
|  | `2775` |  | `result = dataSource_.GetSource();`
|  | `2776` |  | `}`
|  | `2777` |  | ``
|  | `2778` |  | `return result;`
|  | `2779` |  | `}`
|  | `2780` |  | ``
|  | `2781` |  | `#region Instance Fields`
|  | `2782` |  | `ZipEntry entry_;`
|  | `2783` |  | `ZipEntry outEntry_;`
|  | `2784` |  | `UpdateCommand command_;`
|  | `2785` |  | `IStaticDataSource dataSource_;`
|  | `2786` |  | `string filename_;`
|  | `2787` |  | `long sizePatchOffset_ = -1;`
|  | `2788` |  | `long crcPatchOffset_ = -1;`
|  | `2789` |  | `long _offsetBasedSize = -1;`
|  | `2790` |  | `#endregion`
|  | `2791` |  | `}`
|  | `2792` |  | ``
|  | `2793` |  | `#endregion`
|  | `2794` |  | `#endregion`
|  | `2795` |  | ``
|  | `2796` |  | `#region Disposing`
|  | `2797` |  | ``
|  | `2798` |  | `#region IDisposable Members`
|  | `2799` |  | `void IDisposable.Dispose()`
|  | `2800` |  | `{`
|  | `2801` |  | `Close();`
|  | `2802` |  | `}`
|  | `2803` |  | `#endregion`
|  | `2804` |  | ``
|  | `2805` |  | `void DisposeInternal(bool disposing)`
|  | `2806` |  | `{`
|  | `2807` |  | `if (!isDisposed_) {`
|  | `2808` |  | `isDisposed_ = true;`
|  | `2809` |  | `entries_ = new ZipEntry[0];`
|  | `2810` |  | ``
|  | `2811` |  | `if (IsStreamOwner && (baseStream_ != null)) {`
|  | `2812` |  | `lock (baseStream_) {`
|  | `2813` |  | `baseStream_.Close();`
|  | `2814` |  | `}`
|  | `2815` |  | `}`
|  | `2816` |  | ``
|  | `2817` |  | `PostUpdateCleanup();`
|  | `2818` |  | `}`
|  | `2819` |  | `}`
|  | `2820` |  | ``
|  | `2821` |  | `/// <summary>`
|  | `2822` |  | `/// Releases the unmanaged resources used by the this instance and optionally releases the managed resources.`
|  | `2823` |  | `/// </summary>`
|  | `2824` |  | `/// <param name="disposing">true to release both managed and unmanaged resources;`
|  | `2825` |  | `/// false to release only unmanaged resources.</param>`
|  | `2826` |  | `protected virtual void Dispose(bool disposing)`
|  | `2827` |  | `{`
|  | `2828` |  | `DisposeInternal(disposing);`
|  | `2829` |  | `}`
|  | `2830` |  | ``
|  | `2831` |  | `#endregion`
|  | `2832` |  | ``
|  | `2833` |  | `#region Internal routines`
|  | `2834` |  | `#region Reading`
|  | `2835` |  | `/// <summary>`
|  | `2836` |  | `/// Read an unsigned short in little endian byte order.`
|  | `2837` |  | `/// </summary>`
|  | `2838` |  | `/// <returns>Returns the value read.</returns>`
|  | `2839` |  | `/// <exception cref="EndOfStreamException">`
|  | `2840` |  | `/// The stream ends prematurely`
|  | `2841` |  | `/// </exception>`
|  | `2842` |  | `ushort ReadLEUshort()`
|  | `2843` |  | `{`
|  | `2844` |  | `int data1 = baseStream_.ReadByte();`
|  | `2845` |  | ``
|  | `2846` |  | `if (data1 < 0) {`
|  | `2847` |  | `throw new EndOfStreamException("End of stream");`
|  | `2848` |  | `}`
|  | `2849` |  | ``
|  | `2850` |  | `int data2 = baseStream_.ReadByte();`
|  | `2851` |  | ``
|  | `2852` |  | `if (data2 < 0) {`
|  | `2853` |  | `throw new EndOfStreamException("End of stream");`
|  | `2854` |  | `}`
|  | `2855` |  | ``
|  | `2856` |  | ``
|  | `2857` |  | `return unchecked((ushort)((ushort)data1 | (ushort)(data2 << 8)));`
|  | `2858` |  | `}`
|  | `2859` |  | ``
|  | `2860` |  | `/// <summary>`
|  | `2861` |  | `/// Read a uint in little endian byte order.`
|  | `2862` |  | `/// </summary>`
|  | `2863` |  | `/// <returns>Returns the value read.</returns>`
|  | `2864` |  | `/// <exception cref="IOException">`
|  | `2865` |  | `/// An i/o error occurs.`
|  | `2866` |  | `/// </exception>`
|  | `2867` |  | `/// <exception cref="System.IO.EndOfStreamException">`
|  | `2868` |  | `/// The file ends prematurely`
|  | `2869` |  | `/// </exception>`
|  | `2870` |  | `uint ReadLEUint()`
|  | `2871` |  | `{`
|  | `2872` |  | `return (uint)(ReadLEUshort() | (ReadLEUshort() << 16));`
|  | `2873` |  | `}`
|  | `2874` |  | ``
|  | `2875` |  | `ulong ReadLEUlong()`
|  | `2876` |  | `{`
|  | `2877` |  | `return ReadLEUint() | ((ulong)ReadLEUint() << 32);`
|  | `2878` |  | `}`
|  | `2879` |  | ``
|  | `2880` |  | `#endregion`
|  | `2881` |  | `// NOTE this returns the offset of the first byte after the signature.`
|  | `2882` |  | `long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData)`
|  | `2883` |  | `{`
|  | `2884` |  | `using (ZipHelperStream les = new ZipHelperStream(baseStream_)) {`
|  | `2885` |  | `return les.LocateBlockWithSignature(signature, endLocation, minimumBlockSize, maximumVariableData);`
|  | `2886` |  | `}`
|  | `2887` |  | `}`
|  | `2888` |  | ``
|  | `2889` |  | `/// <summary>`
|  | `2890` |  | `/// Search for and read the central directory of a zip file filling the entries array.`
|  | `2891` |  | `/// </summary>`
|  | `2892` |  | `/// <exception cref="System.IO.IOException">`
|  | `2893` |  | `/// An i/o error occurs.`
|  | `2894` |  | `/// </exception>`
|  | `2895` |  | `/// <exception cref="ICSharpCode.SharpZipLib.Zip.ZipException">`
|  | `2896` |  | `/// The central directory is malformed or cannot be found`
|  | `2897` |  | `/// </exception>`
|  | `2898` |  | `void ReadEntries()`
|  | `2899` |  | `{`
|  | `2900` |  | `// Search for the End Of Central Directory. When a zip comment is`
|  | `2901` |  | `// present the directory will start earlier`
|  | `2902` |  | `//`
|  | `2903` |  | `// The search is limited to 64K which is the maximum size of a trailing comment field to aid speed.`
|  | `2904` |  | `// This should be compatible with both SFX and ZIP files but has only been tested for Zip files`
|  | `2905` |  | `// If a SFX file has the Zip data attached as a resource and there are other resources occuring later then`
|  | `2906` |  | `// this could be invalid.`
|  | `2907` |  | `// Could also speed this up by reading memory in larger blocks.`
|  | `2908` |  | ``
|  | `2909` |  | `if (baseStream_.CanSeek == false) {`
|  | `2910` |  | `throw new ZipException("ZipFile stream must be seekable");`
|  | `2911` |  | `}`
|  | `2912` |  | ``
|  | `2913` |  | `long locatedEndOfCentralDir = LocateBlockWithSignature(ZipConstants.EndOfCentralDirectorySignature,`
|  | `2914` |  | `baseStream_.Length, ZipConstants.EndOfCentralRecordBaseSize, 0xffff);`
|  | `2915` |  | ``
|  | `2916` |  | `if (locatedEndOfCentralDir < 0) {`
|  | `2917` |  | `throw new ZipException("Cannot find central directory");`
|  | `2918` |  | `}`
|  | `2919` |  | ``
|  | `2920` |  | `// Read end of central directory record`
|  | `2921` |  | `ushort thisDiskNumber = ReadLEUshort();`
|  | `2922` |  | `ushort startCentralDirDisk = ReadLEUshort();`
|  | `2923` |  | `ulong entriesForThisDisk = ReadLEUshort();`
|  | `2924` |  | `ulong entriesForWholeCentralDir = ReadLEUshort();`
|  | `2925` |  | `ulong centralDirSize = ReadLEUint();`
|  | `2926` |  | `long offsetOfCentralDir = ReadLEUint();`
|  | `2927` |  | `uint commentSize = ReadLEUshort();`
|  | `2928` |  | ``
|  | `2929` |  | `if (commentSize > 0) {`
|  | `2930` |  | `byte[] comment = new byte[commentSize];`
|  | `2931` |  | ``
|  | `2932` |  | `StreamUtils.ReadFully(baseStream_, comment);`
|  | `2933` |  | `comment_ = ZipConstants.ConvertToString(comment);`
|  | `2934` |  | `} else {`
|  | `2935` |  | `comment_ = string.Empty;`
|  | `2936` |  | `}`
|  | `2937` |  | ``
|  | `2938` |  | `bool isZip64 = false;`
|  | `2939` |  | ``
|  | `2940` |  | `// Check if zip64 header information is required.`
|  | `2941` |  | `if ((thisDiskNumber == 0xffff) ||`
|  | `2942` |  | `(startCentralDirDisk == 0xffff) ||`
|  | `2943` |  | `(entriesForThisDisk == 0xffff) ||`
|  | `2944` |  | `(entriesForWholeCentralDir == 0xffff) ||`
|  | `2945` |  | `(centralDirSize == 0xffffffff) ||`
|  | `2946` |  | `(offsetOfCentralDir == 0xffffffff)) {`
|  | `2947` |  | `isZip64 = true;`
|  | `2948` |  | ``
|  | `2949` |  | `long offset = LocateBlockWithSignature(ZipConstants.Zip64CentralDirLocatorSignature, locatedEndOfCentralDir, 0,`
|  | `2950` |  | `if (offset < 0) {`
|  | `2951` |  | `throw new ZipException("Cannot find Zip64 locator");`
|  | `2952` |  | `}`
|  | `2953` |  | ``
|  | `2954` |  | `// number of the disk with the start of the zip64 end of central directory 4 bytes`
|  | `2955` |  | `// relative offset of the zip64 end of central directory record 8 bytes`
|  | `2956` |  | `// total number of disks 4 bytes`
|  | `2957` |  | `ReadLEUint(); // startDisk64 is not currently used`
|  | `2958` |  | `ulong offset64 = ReadLEUlong();`
|  | `2959` |  | `uint totalDisks = ReadLEUint();`
|  | `2960` |  | ``
|  | `2961` |  | `baseStream_.Position = (long)offset64;`
|  | `2962` |  | `long sig64 = ReadLEUint();`
|  | `2963` |  | ``
|  | `2964` |  | `if (sig64 != ZipConstants.Zip64CentralFileHeaderSignature) {`
|  | `2965` |  | `throw new ZipException(string.Format("Invalid Zip64 Central directory signature at {0:X}", offset64));`
|  | `2966` |  | `}`
|  | `2967` |  | ``
|  | `2968` |  | `// NOTE: Record size = SizeOfFixedFields + SizeOfVariableData - 12.`
|  | `2969` |  | `ulong recordSize = ReadLEUlong();`
|  | `2970` |  | `int versionMadeBy = ReadLEUshort();`
|  | `2971` |  | `int versionToExtract = ReadLEUshort();`
|  | `2972` |  | `uint thisDisk = ReadLEUint();`
|  | `2973` |  | `uint centralDirDisk = ReadLEUint();`
|  | `2974` |  | `entriesForThisDisk = ReadLEUlong();`
|  | `2975` |  | `entriesForWholeCentralDir = ReadLEUlong();`
|  | `2976` |  | `centralDirSize = ReadLEUlong();`
|  | `2977` |  | `offsetOfCentralDir = (long)ReadLEUlong();`
|  | `2978` |  | ``
|  | `2979` |  | `// NOTE: zip64 extensible data sector (variable size) is ignored.`
|  | `2980` |  | `}`
|  | `2981` |  | ``
|  | `2982` |  | `entries_ = new ZipEntry[entriesForThisDisk];`
|  | `2983` |  | ``
|  | `2984` |  | `// SFX/embedded support, find the offset of the first entry vis the start of the stream`
|  | `2985` |  | `// This applies to Zip files that are appended to the end of an SFX stub.`
|  | `2986` |  | `// Or are appended as a resource to an executable.`
|  | `2987` |  | `// Zip files created by some archivers have the offsets altered to reflect the true offsets`
|  | `2988` |  | `// and so dont require any adjustment here...`
|  | `2989` |  | `// TODO: Difficulty with Zip64 and SFX offset handling needs resolution - maths?`
|  | `2990` |  | `if (!isZip64 && (offsetOfCentralDir < locatedEndOfCentralDir - (4 + (long)centralDirSize))) {`
|  | `2991` |  | `offsetOfFirstEntry = locatedEndOfCentralDir - (4 + (long)centralDirSize + offsetOfCentralDir);`
|  | `2992` |  | `if (offsetOfFirstEntry <= 0) {`
|  | `2993` |  | `throw new ZipException("Invalid embedded zip archive");`
|  | `2994` |  | `}`
|  | `2995` |  | `}`
|  | `2996` |  | ``
|  | `2997` |  | `baseStream_.Seek(offsetOfFirstEntry + offsetOfCentralDir, SeekOrigin.Begin);`
|  | `2998` |  | ``
|  | `2999` |  | `for (ulong i = 0; i < entriesForThisDisk; i++) {`
|  | `3000` |  | `if (ReadLEUint() != ZipConstants.CentralHeaderSignature) {`
|  | `3001` |  | `throw new ZipException("Wrong Central Directory signature");`
|  | `3002` |  | `}`
|  | `3003` |  | ``
|  | `3004` |  | `int versionMadeBy = ReadLEUshort();`
|  | `3005` |  | `int versionToExtract = ReadLEUshort();`
|  | `3006` |  | `int bitFlags = ReadLEUshort();`
|  | `3007` |  | `int method = ReadLEUshort();`
|  | `3008` |  | `uint dostime = ReadLEUint();`
|  | `3009` |  | `uint crc = ReadLEUint();`
|  | `3010` |  | `var csize = (long)ReadLEUint();`
|  | `3011` |  | `var size = (long)ReadLEUint();`
|  | `3012` |  | `int nameLen = ReadLEUshort();`
|  | `3013` |  | `int extraLen = ReadLEUshort();`
|  | `3014` |  | `int commentLen = ReadLEUshort();`
|  | `3015` |  | ``
|  | `3016` |  | `int diskStartNo = ReadLEUshort(); // Not currently used`
|  | `3017` |  | `int internalAttributes = ReadLEUshort(); // Not currently used`
|  | `3018` |  | ``
|  | `3019` |  | `uint externalAttributes = ReadLEUint();`
|  | `3020` |  | `long offset = ReadLEUint();`
|  | `3021` |  | ``
|  | `3022` |  | `byte[] buffer = new byte[Math.Max(nameLen, commentLen)];`
|  | `3023` |  | ``
|  | `3024` |  | `StreamUtils.ReadFully(baseStream_, buffer, 0, nameLen);`
|  | `3025` |  | `string name = ZipConstants.ConvertToStringExt(bitFlags, buffer, nameLen);`
|  | `3026` |  | ``
|  | `3027` |  | `var entry = new ZipEntry(name, versionToExtract, versionMadeBy, (CompressionMethod)method);`
|  | `3028` |  | `entry.Crc = crc & 0xffffffffL;`
|  | `3029` |  | `entry.Size = size & 0xffffffffL;`
|  | `3030` |  | `entry.CompressedSize = csize & 0xffffffffL;`
|  | `3031` |  | `entry.Flags = bitFlags;`
|  | `3032` |  | `entry.DosTime = (uint)dostime;`
|  | `3033` |  | `entry.ZipFileIndex = (long)i;`
|  | `3034` |  | `entry.Offset = offset;`
|  | `3035` |  | `entry.ExternalFileAttributes = (int)externalAttributes;`
|  | `3036` |  | ``
|  | `3037` |  | `if ((bitFlags & 8) == 0) {`
|  | `3038` |  | `entry.CryptoCheckValue = (byte)(crc >> 24);`
|  | `3039` |  | `} else {`
|  | `3040` |  | `entry.CryptoCheckValue = (byte)((dostime >> 8) & 0xff);`
|  | `3041` |  | `}`
|  | `3042` |  | ``
|  | `3043` |  | `if (extraLen > 0) {`
|  | `3044` |  | `byte[] extra = new byte[extraLen];`
|  | `3045` |  | `StreamUtils.ReadFully(baseStream_, extra);`
|  | `3046` |  | `entry.ExtraData = extra;`
|  | `3047` |  | `}`
|  | `3048` |  | ``
|  | `3049` |  | `entry.ProcessExtraData(false);`
|  | `3050` |  | ``
|  | `3051` |  | `if (commentLen > 0) {`
|  | `3052` |  | `StreamUtils.ReadFully(baseStream_, buffer, 0, commentLen);`
|  | `3053` |  | `entry.Comment = ZipConstants.ConvertToStringExt(bitFlags, buffer, commentLen);`
|  | `3054` |  | `}`
|  | `3055` |  | ``
|  | `3056` |  | `entries_[i] = entry;`
|  | `3057` |  | `}`
|  | `3058` |  | `}`
|  | `3059` |  | ``
|  | `3060` |  | `/// <summary>`
|  | `3061` |  | `/// Locate the data for a given entry.`
|  | `3062` |  | `/// </summary>`
|  | `3063` |  | `/// <returns>`
|  | `3064` |  | `/// The start offset of the data.`
|  | `3065` |  | `/// </returns>`
|  | `3066` |  | `/// <exception cref="System.IO.EndOfStreamException">`
|  | `3067` |  | `/// The stream ends prematurely`
|  | `3068` |  | `/// </exception>`
|  | `3069` |  | `/// <exception cref="ICSharpCode.SharpZipLib.Zip.ZipException">`
|  | `3070` |  | `/// The local header signature is invalid, the entry and central header file name lengths are different`
|  | `3071` |  | `/// or the local and entry compression methods dont match`
|  | `3072` |  | `/// </exception>`
|  | `3073` |  | `long LocateEntry(ZipEntry entry)`
|  | `3074` |  | `{`
|  | `3075` |  | `return TestLocalHeader(entry, HeaderTest.Extract);`
|  | `3076` |  | `}`
|  | `3077` |  | ``
|  | `3078` |  | `Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry)`
|  | `3079` |  | `{`
|  | `3080` |  | `CryptoStream result = null;`
|  | `3081` |  | ``
|  | `3082` |  | `if ((entry.Version < ZipConstants.VersionStrongEncryption)`
|  | `3083` |  | `|| (entry.Flags & (int)GeneralBitFlags.StrongEncryption) == 0) {`
|  | `3084` |  | `var classicManaged = new PkzipClassicManaged();`
|  | `3085` |  | ``
|  | `3086` |  | `OnKeysRequired(entry.Name);`
|  | `3087` |  | `if (HaveKeys == false) {`
|  | `3088` |  | `throw new ZipException("No password available for encrypted stream");`
|  | `3089` |  | `}`
|  | `3090` |  | ``
|  | `3091` |  | `result = new CryptoStream(baseStream, classicManaged.CreateDecryptor(key, null), CryptoStreamMode.Read);`
|  | `3092` |  | `CheckClassicPassword(result, entry);`
|  | `3093` |  | `} else {`
|  | `3094` |  | `if (entry.Version == ZipConstants.VERSION_AES) {`
|  | `3095` |  | `//`
|  | `3096` |  | `OnKeysRequired(entry.Name);`
|  | `3097` |  | `if (HaveKeys == false) {`
|  | `3098` |  | `throw new ZipException("No password available for AES encrypted stream");`
|  | `3099` |  | `}`
|  | `3100` |  | `int saltLen = entry.AESSaltLen;`
|  | `3101` |  | `byte[] saltBytes = new byte[saltLen];`
|  | `3102` |  | `int saltIn = baseStream.Read(saltBytes, 0, saltLen);`
|  | `3103` |  | `if (saltIn != saltLen)`
|  | `3104` |  | `throw new ZipException("AES Salt expected " + saltLen + " got " + saltIn);`
|  | `3105` |  | `//`
|  | `3106` |  | `byte[] pwdVerifyRead = new byte[2];`
|  | `3107` |  | `baseStream.Read(pwdVerifyRead, 0, 2);`
|  | `3108` |  | `int blockSize = entry.AESKeySize / 8; // bits to bytes`
|  | `3109` |  | ``
|  | `3110` |  | `var decryptor = new ZipAESTransform(rawPassword_, saltBytes, blockSize, false);`
|  | `3111` |  | `byte[] pwdVerifyCalc = decryptor.PwdVerifier;`
|  | `3112` |  | `if (pwdVerifyCalc[0] != pwdVerifyRead[0] || pwdVerifyCalc[1] != pwdVerifyRead[1])`
|  | `3113` |  | `throw new ZipException("Invalid password for AES");`
|  | `3114` |  | `result = new ZipAESStream(baseStream, decryptor, CryptoStreamMode.Read);`
|  | `3115` |  | `} else {`
|  | `3116` |  | `throw new ZipException("Decryption method not supported");`
|  | `3117` |  | `}`
|  | `3118` |  | `}`
|  | `3119` |  | ``
|  | `3120` |  | `return result;`
|  | `3121` |  | `}`
|  | `3122` |  | ``
|  | `3123` |  | `Stream CreateAndInitEncryptionStream(Stream baseStream, ZipEntry entry)`
|  | `3124` |  | `{`
|  | `3125` |  | `CryptoStream result = null;`
|  | `3126` |  | `if ((entry.Version < ZipConstants.VersionStrongEncryption)`
|  | `3127` |  | `|| (entry.Flags & (int)GeneralBitFlags.StrongEncryption) == 0) {`
|  | `3128` |  | `var classicManaged = new PkzipClassicManaged();`
|  | `3129` |  | ``
|  | `3130` |  | `OnKeysRequired(entry.Name);`
|  | `3131` |  | `if (HaveKeys == false) {`
|  | `3132` |  | `throw new ZipException("No password available for encrypted stream");`
|  | `3133` |  | `}`
|  | `3134` |  | ``
|  | `3135` |  | `// Closing a CryptoStream will close the base stream as well so wrap it in an UncompressedStream`
|  | `3136` |  | `// which doesnt do this.`
|  | `3137` |  | `result = new CryptoStream(new UncompressedStream(baseStream),`
|  | `3138` |  | `classicManaged.CreateEncryptor(key, null), CryptoStreamMode.Write);`
|  | `3139` |  | ``
|  | `3140` |  | `if ((entry.Crc < 0) || (entry.Flags & 8) != 0) {`
|  | `3141` |  | `WriteEncryptionHeader(result, entry.DosTime << 16);`
|  | `3142` |  | `} else {`
|  | `3143` |  | `WriteEncryptionHeader(result, entry.Crc);`
|  | `3144` |  | `}`
|  | `3145` |  | `}`
|  | `3146` |  | `return result;`
|  | `3147` |  | `}`
|  | `3148` |  | ``
|  | `3149` |  | `static void CheckClassicPassword(CryptoStream classicCryptoStream, ZipEntry entry)`
|  | `3150` |  | `{`
|  | `3151` |  | `byte[] cryptbuffer = new byte[ZipConstants.CryptoHeaderSize];`
|  | `3152` |  | `StreamUtils.ReadFully(classicCryptoStream, cryptbuffer);`
|  | `3153` |  | `if (cryptbuffer[ZipConstants.CryptoHeaderSize - 1] != entry.CryptoCheckValue) {`
|  | `3154` |  | `throw new ZipException("Invalid password");`
|  | `3155` |  | `}`
|  | `3156` |  | `}`
|  | `3157` |  | ``
|  | `3158` |  | `static void WriteEncryptionHeader(Stream stream, long crcValue)`
|  | `3159` |  | `{`
|  | `3160` |  | `byte[] cryptBuffer = new byte[ZipConstants.CryptoHeaderSize];`
|  | `3161` |  | `var rnd = new Random();`
|  | `3162` |  | `rnd.NextBytes(cryptBuffer);`
|  | `3163` |  | `cryptBuffer[11] = (byte)(crcValue >> 24);`
|  | `3164` |  | `stream.Write(cryptBuffer, 0, cryptBuffer.Length);`
|  | `3165` |  | `}`
|  | `3166` |  | ``
|  | `3167` |  | `#endregion`
|  | `3168` |  | ``
|  | `3169` |  | `#region Instance Fields`
|  | `3170` |  | `bool isDisposed_;`
|  | `3171` |  | `string name_;`
|  | `3172` |  | `string comment_;`
|  | `3173` |  | `string rawPassword_;`
|  | `3174` |  | `Stream baseStream_;`
|  | `3175` |  | `bool isStreamOwner;`
|  | `3176` |  | `long offsetOfFirstEntry;`
|  | `3177` |  | `ZipEntry[] entries_;`
|  | `3178` |  | `byte[] key;`
|  | `3179` |  | `bool isNewArchive_;`
|  | `3180` |  | ``
|  | `3181` |  | `// Default is dynamic which is not backwards compatible and can cause problems`
|  | `3182` |  | `// with XP's built in compression which cant read Zip64 archives.`
|  | `3183` |  | `// However it does avoid the situation were a large file is added and cannot be completed correctly.`
|  | `3184` |  | `// Hint: Set always ZipEntry size before they are added to an archive and this setting isnt needed.`
|  | `3185` |  | `UseZip64 useZip64_ = UseZip64.Dynamic;`
|  | `3186` |  | ``
|  | `3187` |  | `#region Zip Update Instance Fields`
|  | `3188` |  | `ArrayList updates_;`
|  | `3189` |  | `long updateCount_; // Count is managed manually as updates_ can contain nulls!`
|  | `3190` |  | `Hashtable updateIndex_;`
|  | `3191` |  | `IArchiveStorage archiveStorage_;`
|  | `3192` |  | `IDynamicDataSource updateDataSource_;`
|  | `3193` |  | `bool contentsEdited_;`
|  | `3194` |  | `int bufferSize_ = DefaultBufferSize;`
|  | `3195` |  | `byte[] copyBuffer_;`
|  | `3196` |  | `ZipString newComment_;`
|  | `3197` |  | `bool commentEdited_;`
|  | `3198` |  | `IEntryFactory updateEntryFactory_ = new ZipEntryFactory();`
|  | `3199` |  | `#endregion`
|  | `3200` |  | `#endregion`
|  | `3201` |  | ``
|  | `3202` |  | `#region Support Classes`
|  | `3203` |  | `/// <summary>`
|  | `3204` |  | `/// Represents a string from a <see cref="ZipFile"/> which is stored as an array of bytes.`
|  | `3205` |  | `/// </summary>`
|  | `3206` |  | `class ZipString`
|  | `3207` |  | `{`
|  | `3208` |  | `#region Constructors`
|  | `3209` |  | `/// <summary>`
|  | `3210` |  | `/// Initialise a <see cref="ZipString"/> with a string.`
|  | `3211` |  | `/// </summary>`
|  | `3212` |  | `/// <param name="comment">The textual string form.</param>`
|  | `3213` |  | `public ZipString(string comment)`
|  | `3214` |  | `{`
|  | `3215` |  | `comment_ = comment;`
|  | `3216` |  | `isSourceString_ = true;`
|  | `3217` |  | `}`
|  | `3218` |  | ``
|  | `3219` |  | `/// <summary>`
|  | `3220` |  | `/// Initialise a <see cref="ZipString"/> using a string in its binary 'raw' form.`
|  | `3221` |  | `/// </summary>`
|  | `3222` |  | `/// <param name="rawString"></param>`
|  | `3223` |  | `public ZipString(byte[] rawString)`
|  | `3224` |  | `{`
|  | `3225` |  | `rawComment_ = rawString;`
|  | `3226` |  | `}`
|  | `3227` |  | `#endregion`
|  | `3228` |  | ``
|  | `3229` |  | `/// <summary>`
|  | `3230` |  | `/// Get a value indicating the original source of data for this instance.`
|  | `3231` |  | `/// True if the source was a string; false if the source was binary data.`
|  | `3232` |  | `/// </summary>`
|  | `3233` |  | `public bool IsSourceString {`
|  | `3234` |  | `get { return isSourceString_; }`
|  | `3235` |  | `}`
|  | `3236` |  | ``
|  | `3237` |  | `/// <summary>`
|  | `3238` |  | `/// Get the length of the comment when represented as raw bytes.`
|  | `3239` |  | `/// </summary>`
|  | `3240` |  | `public int RawLength {`
|  | `3241` |  | `get {`
|  | `3242` |  | `MakeBytesAvailable();`
|  | `3243` |  | `return rawComment_.Length;`
|  | `3244` |  | `}`
|  | `3245` |  | `}`
|  | `3246` |  | ``
|  | `3247` |  | `/// <summary>`
|  | `3248` |  | `/// Get the comment in its 'raw' form as plain bytes.`
|  | `3249` |  | `/// </summary>`
|  | `3250` |  | `public byte[] RawComment {`
|  | `3251` |  | `get {`
|  | `3252` |  | `MakeBytesAvailable();`
|  | `3253` |  | `return (byte[])rawComment_.Clone();`
|  | `3254` |  | `}`
|  | `3255` |  | `}`
|  | `3256` |  | ``
|  | `3257` |  | `/// <summary>`
|  | `3258` |  | `/// Reset the comment to its initial state.`
|  | `3259` |  | `/// </summary>`
|  | `3260` |  | `public void Reset()`
|  | `3261` |  | `{`
|  | `3262` |  | `if (isSourceString_) {`
|  | `3263` |  | `rawComment_ = null;`
|  | `3264` |  | `} else {`
|  | `3265` |  | `comment_ = null;`
|  | `3266` |  | `}`
|  | `3267` |  | `}`
|  | `3268` |  | ``
|  | `3269` |  | `void MakeTextAvailable()`
|  | `3270` |  | `{`
|  | `3271` |  | `if (comment_ == null) {`
|  | `3272` |  | `comment_ = ZipConstants.ConvertToString(rawComment_);`
|  | `3273` |  | `}`
|  | `3274` |  | `}`
|  | `3275` |  | ``
|  | `3276` |  | `void MakeBytesAvailable()`
|  | `3277` |  | `{`
|  | `3278` |  | `if (rawComment_ == null) {`
|  | `3279` |  | `rawComment_ = ZipConstants.ConvertToArray(comment_);`
|  | `3280` |  | `}`
|  | `3281` |  | `}`
|  | `3282` |  | ``
|  | `3283` |  | `/// <summary>`
|  | `3284` |  | `/// Implicit conversion of comment to a string.`
|  | `3285` |  | `/// </summary>`
|  | `3286` |  | `/// <param name="zipString">The <see cref="ZipString"/> to convert to a string.</param>`
|  | `3287` |  | `/// <returns>The textual equivalent for the input value.</returns>`
|  | `3288` |  | `static public implicit operator string(ZipString zipString)`
|  | `3289` |  | `{`
|  | `3290` |  | `zipString.MakeTextAvailable();`
|  | `3291` |  | `return zipString.comment_;`
|  | `3292` |  | `}`
|  | `3293` |  | ``
|  | `3294` |  | `#region Instance Fields`
|  | `3295` |  | `string comment_;`
|  | `3296` |  | `byte[] rawComment_;`
|  | `3297` |  | `bool isSourceString_;`
|  | `3298` |  | `#endregion`
|  | `3299` |  | `}`
|  | `3300` |  | ``
|  | `3301` |  | `/// <summary>`
|  | `3302` |  | `/// An <see cref="IEnumerator">enumerator</see> for <see cref="ZipEntry">Zip entries</see>`
|  | `3303` |  | `/// </summary>`
|  | `3304` |  | `class ZipEntryEnumerator : IEnumerator`
|  | `3305` |  | `{`
|  | `3306` |  | `#region Constructors`
|  | `3307` |  | `public ZipEntryEnumerator(ZipEntry[] entries)`
|  | `3308` |  | `{`
|  | `3309` |  | `array = entries;`
|  | `3310` |  | `}`
|  | `3311` |  | ``
|  | `3312` |  | `#endregion`
|  | `3313` |  | `#region IEnumerator Members`
|  | `3314` |  | `public object Current {`
|  | `3315` |  | `get {`
|  | `3316` |  | `return array[index];`
|  | `3317` |  | `}`
|  | `3318` |  | `}`
|  | `3319` |  | ``
|  | `3320` |  | `public void Reset()`
|  | `3321` |  | `{`
|  | `3322` |  | `index = -1;`
|  | `3323` |  | `}`
|  | `3324` |  | ``
|  | `3325` |  | `public bool MoveNext()`
|  | `3326` |  | `{`
|  | `3327` |  | `return (++index < array.Length);`
|  | `3328` |  | `}`
|  | `3329` |  | `#endregion`
|  | `3330` |  | `#region Instance Fields`
|  | `3331` |  | `ZipEntry[] array;`
|  | `3332` |  | `int index = -1;`
|  | `3333` |  | `#endregion`
|  | `3334` |  | `}`
|  | `3335` |  | ``
|  | `3336` |  | `/// <summary>`
|  | `3337` |  | `/// An <see cref="UncompressedStream"/> is a stream that you can write uncompressed data`
|  | `3338` |  | `/// to and flush, but cannot read, seek or do anything else to.`
|  | `3339` |  | `/// </summary>`
|  | `3340` |  | `class UncompressedStream : Stream`
|  | `3341` |  | `{`
|  | `3342` |  | `#region Constructors`
|  | `3343` |  | `public UncompressedStream(Stream baseStream)`
|  | `3344` |  | `{`
|  | `3345` |  | `baseStream_ = baseStream;`
|  | `3346` |  | `}`
|  | `3347` |  | ``
|  | `3348` |  | `#endregion`
|  | `3349` |  | ``
|  | `3350` |  | `/// <summary>`
|  | `3351` |  | `/// Close this stream instance.`
|  | `3352` |  | `/// </summary>`
|  | `3353` |  | `public override void Close()`
|  | `3354` |  | `{`
|  | `3355` |  | `// Do nothing`
|  | `3356` |  | `}`
|  | `3357` |  | ``
|  | `3358` |  | `/// <summary>`
|  | `3359` |  | `/// Gets a value indicating whether the current stream supports reading.`
|  | `3360` |  | `/// </summary>`
|  | `3361` |  | `public override bool CanRead {`
|  | `3362` |  | `get {`
|  | `3363` |  | `return false;`
|  | `3364` |  | `}`
|  | `3365` |  | `}`
|  | `3366` |  | ``
|  | `3367` |  | `/// <summary>`
|  | `3368` |  | `/// Write any buffered data to underlying storage.`
|  | `3369` |  | `/// </summary>`
|  | `3370` |  | `public override void Flush()`
|  | `3371` |  | `{`
|  | `3372` |  | `baseStream_.Flush();`
|  | `3373` |  | `}`
|  | `3374` |  | ``
|  | `3375` |  | `/// <summary>`
|  | `3376` |  | `/// Gets a value indicating whether the current stream supports writing.`
|  | `3377` |  | `/// </summary>`
|  | `3378` |  | `public override bool CanWrite {`
|  | `3379` |  | `get {`
|  | `3380` |  | `return baseStream_.CanWrite;`
|  | `3381` |  | `}`
|  | `3382` |  | `}`
|  | `3383` |  | ``
|  | `3384` |  | `/// <summary>`
|  | `3385` |  | `/// Gets a value indicating whether the current stream supports seeking.`
|  | `3386` |  | `/// </summary>`
|  | `3387` |  | `public override bool CanSeek {`
|  | `3388` |  | `get {`
|  | `3389` |  | `return false;`
|  | `3390` |  | `}`
|  | `3391` |  | `}`
|  | `3392` |  | ``
|  | `3393` |  | `/// <summary>`
|  | `3394` |  | `/// Get the length in bytes of the stream.`
|  | `3395` |  | `/// </summary>`
|  | `3396` |  | `public override long Length {`
|  | `3397` |  | `get {`
|  | `3398` |  | `return 0;`
|  | `3399` |  | `}`
|  | `3400` |  | `}`
|  | `3401` |  | ``
|  | `3402` |  | `/// <summary>`
|  | `3403` |  | `/// Gets or sets the position within the current stream.`
|  | `3404` |  | `/// </summary>`
|  | `3405` |  | `public override long Position {`
|  | `3406` |  | `get {`
|  | `3407` |  | `return baseStream_.Position;`
|  | `3408` |  | `}`
|  | `3409` |  | `set {`
|  | `3410` |  | `throw new NotImplementedException();`
|  | `3411` |  | `}`
|  | `3412` |  | `}`
|  | `3413` |  | ``
|  | `3414` |  | `/// <summary>`
|  | `3415` |  | `/// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of`
|  | `3416` |  | `/// </summary>`
|  | `3417` |  | `/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array`
|  | `3418` |  | `/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the cur`
|  | `3419` |  | `/// <param name="count">The maximum number of bytes to be read from the current stream.</param>`
|  | `3420` |  | `/// <returns>`
|  | `3421` |  | `/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that ma`
|  | `3422` |  | `/// </returns>`
|  | `3423` |  | `/// <exception cref="T:System.ArgumentException">The sum of offset and count is larger than the buffer length. </e`
|  | `3424` |  | `/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exceptio`
|  | `3425` |  | `/// <exception cref="T:System.NotSupportedException">The stream does not support reading. </exception>`
|  | `3426` |  | `/// <exception cref="T:System.ArgumentNullException">buffer is null. </exception>`
|  | `3427` |  | `/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>`
|  | `3428` |  | `/// <exception cref="T:System.ArgumentOutOfRangeException">offset or count is negative. </exception>`
|  | `3429` |  | `public override int Read(byte[] buffer, int offset, int count)`
|  | `3430` |  | `{`
|  | `3431` |  | `return 0;`
|  | `3432` |  | `}`
|  | `3433` |  | ``
|  | `3434` |  | `/// <summary>`
|  | `3435` |  | `/// Sets the position within the current stream.`
|  | `3436` |  | `/// </summary>`
|  | `3437` |  | `/// <param name="offset">A byte offset relative to the origin parameter.</param>`
|  | `3438` |  | `/// <param name="origin">A value of type <see cref="T:System.IO.SeekOrigin"></see> indicating the reference point`
|  | `3439` |  | `/// <returns>`
|  | `3440` |  | `/// The new position within the current stream.`
|  | `3441` |  | `/// </returns>`
|  | `3442` |  | `/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>`
|  | `3443` |  | `/// <exception cref="T:System.NotSupportedException">The stream does not support seeking, such as if the stream is`
|  | `3444` |  | `/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exceptio`
|  | `3445` |  | `public override long Seek(long offset, SeekOrigin origin)`
|  | `3446` |  | `{`
|  | `3447` |  | `return 0;`
|  | `3448` |  | `}`
|  | `3449` |  | ``
|  | `3450` |  | `/// <summary>`
|  | `3451` |  | `/// Sets the length of the current stream.`
|  | `3452` |  | `/// </summary>`
|  | `3453` |  | `/// <param name="value">The desired length of the current stream in bytes.</param>`
|  | `3454` |  | `/// <exception cref="T:System.NotSupportedException">The stream does not support both writing and seeking, such as`
|  | `3455` |  | `/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>`
|  | `3456` |  | `/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exceptio`
|  | `3457` |  | `public override void SetLength(long value)`
|  | `3458` |  | `{`
|  | `3459` |  | `}`
|  | `3460` |  | ``
|  | `3461` |  | `/// <summary>`
|  | `3462` |  | `/// Writes a sequence of bytes to the current stream and advances the current position within this stream by the n`
|  | `3463` |  | `/// </summary>`
|  | `3464` |  | `/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</par`
|  | `3465` |  | `/// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current strea`
|  | `3466` |  | `/// <param name="count">The number of bytes to be written to the current stream.</param>`
|  | `3467` |  | `/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>`
|  | `3468` |  | `/// <exception cref="T:System.NotSupportedException">The stream does not support writing. </exception>`
|  | `3469` |  | `/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exceptio`
|  | `3470` |  | `/// <exception cref="T:System.ArgumentNullException">buffer is null. </exception>`
|  | `3471` |  | `/// <exception cref="T:System.ArgumentException">The sum of offset and count is greater than the buffer length. </`
|  | `3472` |  | `/// <exception cref="T:System.ArgumentOutOfRangeException">offset or count is negative. </exception>`
|  | `3473` |  | `public override void Write(byte[] buffer, int offset, int count)`
|  | `3474` |  | `{`
|  | `3475` |  | `baseStream_.Write(buffer, offset, count);`
|  | `3476` |  | `}`
|  | `3477` |  | ``
|  | `3478` |  | `readonly`
|  | `3479` |  | ``
|  | `3480` |  | `#region Instance Fields`
|  | `3481` |  | `Stream baseStream_;`
|  | `3482` |  | `#endregion`
|  | `3483` |  | `}`
|  | `3484` |  | ``
|  | `3485` |  | `/// <summary>`
|  | `3486` |  | `/// A <see cref="PartialInputStream"/> is an <see cref="InflaterInputStream"/>`
|  | `3487` |  | `/// whose data is only a part or subsection of a file.`
|  | `3488` |  | `/// </summary>`
|  | `3489` |  | `class PartialInputStream : Stream`
|  | `3490` |  | `{`
|  | `3491` |  | `#region Constructors`
|  | `3492` |  | `/// <summary>`
|  | `3493` |  | `/// Initialise a new instance of the <see cref="PartialInputStream"/> class.`
|  | `3494` |  | `/// </summary>`
|  | `3495` |  | `/// <param name="zipFile">The <see cref="ZipFile"/> containing the underlying stream to use for IO.</param>`
|  | `3496` |  | `/// <param name="start">The start of the partial data.</param>`
|  | `3497` |  | `/// <param name="length">The length of the partial data.</param>`
|  | `3498` |  | `public PartialInputStream(ZipFile zipFile, long start, long length)`
|  | `3499` |  | `{`
|  | `3500` |  | `start_ = start;`
|  | `3501` |  | `length_ = length;`
|  | `3502` |  | ``
|  | `3503` |  | `// Although this is the only time the zipfile is used`
|  | `3504` |  | `// keeping a reference here prevents premature closure of`
|  | `3505` |  | `// this zip file and thus the baseStream_.`
|  | `3506` |  | ``
|  | `3507` |  | `// Code like this will cause apparently random failures depending`
|  | `3508` |  | `// on the size of the files and when garbage is collected.`
|  | `3509` |  | `//`
|  | `3510` |  | `// ZipFile z = new ZipFile (stream);`
|  | `3511` |  | `// Stream reader = z.GetInputStream(0);`
|  | `3512` |  | `// uses reader here....`
|  | `3513` |  | `zipFile_ = zipFile;`
|  | `3514` |  | `baseStream_ = zipFile_.baseStream_;`
|  | `3515` |  | `readPos_ = start;`
|  | `3516` |  | `end_ = start + length;`
|  | `3517` |  | `}`
|  | `3518` |  | `#endregion`
|  | `3519` |  | ``
|  | `3520` |  | `/// <summary>`
|  | `3521` |  | `/// Read a byte from this stream.`
|  | `3522` |  | `/// </summary>`
|  | `3523` |  | `/// <returns>Returns the byte read or -1 on end of stream.</returns>`
|  | `3524` |  | `public override int ReadByte()`
|  | `3525` |  | `{`
|  | `3526` |  | `if (readPos_ >= end_) {`
|  | `3527` |  | `// -1 is the correct value at end of stream.`
|  | `3528` |  | `return -1;`
|  | `3529` |  | `}`
|  | `3530` |  | ``
|  | `3531` |  | `lock (baseStream_) {`
|  | `3532` |  | `baseStream_.Seek(readPos_++, SeekOrigin.Begin);`
|  | `3533` |  | `return baseStream_.ReadByte();`
|  | `3534` |  | `}`
|  | `3535` |  | `}`
|  | `3536` |  | ``
|  | `3537` |  | `/// <summary>`
|  | `3538` |  | `/// Close this <see cref="PartialInputStream">partial input stream</see>.`
|  | `3539` |  | `/// </summary>`
|  | `3540` |  | `/// <remarks>`
|  | `3541` |  | `/// The underlying stream is not closed. Close the parent ZipFile class to do that.`
|  | `3542` |  | `/// </remarks>`
|  | `3543` |  | `public override void Close()`
|  | `3544` |  | `{`
|  | `3545` |  | `// Do nothing at all!`
|  | `3546` |  | `}`
|  | `3547` |  | ``
|  | `3548` |  | `/// <summary>`
|  | `3549` |  | `/// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of`
|  | `3550` |  | `/// </summary>`
|  | `3551` |  | `/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array`
|  | `3552` |  | `/// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the cur`
|  | `3553` |  | `/// <param name="count">The maximum number of bytes to be read from the current stream.</param>`
|  | `3554` |  | `/// <returns>`
|  | `3555` |  | `/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that ma`
|  | `3556` |  | `/// </returns>`
|  | `3557` |  | `/// <exception cref="T:System.ArgumentException">The sum of offset and count is larger than the buffer length. </e`
|  | `3558` |  | `/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exceptio`
|  | `3559` |  | `/// <exception cref="T:System.NotSupportedException">The stream does not support reading. </exception>`
|  | `3560` |  | `/// <exception cref="T:System.ArgumentNullException">buffer is null. </exception>`
|  | `3561` |  | `/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>`
|  | `3562` |  | `/// <exception cref="T:System.ArgumentOutOfRangeException">offset or count is negative. </exception>`
|  | `3563` |  | `public override int Read(byte[] buffer, int offset, int count)`
|  | `3564` |  | `{`
|  | `3565` |  | `lock (baseStream_) {`
|  | `3566` |  | `if (count > end_ - readPos_) {`
|  | `3567` |  | `count = (int)(end_ - readPos_);`
|  | `3568` |  | `if (count == 0) {`
|  | `3569` |  | `return 0;`
|  | `3570` |  | `}`
|  | `3571` |  | `}`
|  | `3572` |  | `// Protect against Stream implementations that throw away their buffer on every Seek`
|  | `3573` |  | `// (for example, Mono FileStream)`
|  | `3574` |  | `if (baseStream_.Position != readPos_) {`
|  | `3575` |  | `baseStream_.Seek(readPos_, SeekOrigin.Begin);`
|  | `3576` |  | `}`
|  | `3577` |  | `int readCount = baseStream_.Read(buffer, offset, count);`
|  | `3578` |  | `if (readCount > 0) {`
|  | `3579` |  | `readPos_ += readCount;`
|  | `3580` |  | `}`
|  | `3581` |  | `return readCount;`
|  | `3582` |  | `}`
|  | `3583` |  | `}`
|  | `3584` |  | ``
|  | `3585` |  | `/// <summary>`
|  | `3586` |  | `/// Writes a sequence of bytes to the current stream and advances the current position within this stream by the n`
|  | `3587` |  | `/// </summary>`
|  | `3588` |  | `/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</par`
|  | `3589` |  | `/// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current strea`
|  | `3590` |  | `/// <param name="count">The number of bytes to be written to the current stream.</param>`
|  | `3591` |  | `/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>`
|  | `3592` |  | `/// <exception cref="T:System.NotSupportedException">The stream does not support writing. </exception>`
|  | `3593` |  | `/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exceptio`
|  | `3594` |  | `/// <exception cref="T:System.ArgumentNullException">buffer is null. </exception>`
|  | `3595` |  | `/// <exception cref="T:System.ArgumentException">The sum of offset and count is greater than the buffer length. </`
|  | `3596` |  | `/// <exception cref="T:System.ArgumentOutOfRangeException">offset or count is negative. </exception>`
|  | `3597` |  | `public override void Write(byte[] buffer, int offset, int count)`
|  | `3598` |  | `{`
|  | `3599` |  | `throw new NotSupportedException();`
|  | `3600` |  | `}`
|  | `3601` |  | ``
|  | `3602` |  | `/// <summary>`
|  | `3603` |  | `/// When overridden in a derived class, sets the length of the current stream.`
|  | `3604` |  | `/// </summary>`
|  | `3605` |  | `/// <param name="value">The desired length of the current stream in bytes.</param>`
|  | `3606` |  | `/// <exception cref="T:System.NotSupportedException">The stream does not support both writing and seeking, such as`
|  | `3607` |  | `/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>`
|  | `3608` |  | `/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exceptio`
|  | `3609` |  | `public override void SetLength(long value)`
|  | `3610` |  | `{`
|  | `3611` |  | `throw new NotSupportedException();`
|  | `3612` |  | `}`
|  | `3613` |  | ``
|  | `3614` |  | `/// <summary>`
|  | `3615` |  | `/// When overridden in a derived class, sets the position within the current stream.`
|  | `3616` |  | `/// </summary>`
|  | `3617` |  | `/// <param name="offset">A byte offset relative to the origin parameter.</param>`
|  | `3618` |  | `/// <param name="origin">A value of type <see cref="T:System.IO.SeekOrigin"></see> indicating the reference point`
|  | `3619` |  | `/// <returns>`
|  | `3620` |  | `/// The new position within the current stream.`
|  | `3621` |  | `/// </returns>`
|  | `3622` |  | `/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>`
|  | `3623` |  | `/// <exception cref="T:System.NotSupportedException">The stream does not support seeking, such as if the stream is`
|  | `3624` |  | `/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exceptio`
|  | `3625` |  | `public override long Seek(long offset, SeekOrigin origin)`
|  | `3626` |  | `{`
|  | `3627` |  | `long newPos = readPos_;`
|  | `3628` |  | ``
|  | `3629` |  | `switch (origin) {`
|  | `3630` |  | `case SeekOrigin.Begin:`
|  | `3631` |  | `newPos = start_ + offset;`
|  | `3632` |  | `break;`
|  | `3633` |  | ``
|  | `3634` |  | `case SeekOrigin.Current:`
|  | `3635` |  | `newPos = readPos_ + offset;`
|  | `3636` |  | `break;`
|  | `3637` |  | ``
|  | `3638` |  | `case SeekOrigin.End:`
|  | `3639` |  | `newPos = end_ + offset;`
|  | `3640` |  | `break;`
|  | `3641` |  | `}`
|  | `3642` |  | ``
|  | `3643` |  | `if (newPos < start_) {`
|  | `3644` |  | `throw new ArgumentException("Negative position is invalid");`
|  | `3645` |  | `}`
|  | `3646` |  | ``
|  | `3647` |  | `if (newPos >= end_) {`
|  | `3648` |  | `throw new IOException("Cannot seek past end");`
|  | `3649` |  | `}`
|  | `3650` |  | `readPos_ = newPos;`
|  | `3651` |  | `return readPos_;`
|  | `3652` |  | `}`
|  | `3653` |  | ``
|  | `3654` |  | `/// <summary>`
|  | `3655` |  | `/// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.`
|  | `3656` |  | `/// </summary>`
|  | `3657` |  | `/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>`
|  | `3658` |  | `public override void Flush()`
|  | `3659` |  | `{`
|  | `3660` |  | `// Nothing to do.`
|  | `3661` |  | `}`
|  | `3662` |  | ``
|  | `3663` |  | `/// <summary>`
|  | `3664` |  | `/// Gets or sets the position within the current stream.`
|  | `3665` |  | `/// </summary>`
|  | `3666` |  | `/// <value></value>`
|  | `3667` |  | `/// <returns>The current position within the stream.</returns>`
|  | `3668` |  | `/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>`
|  | `3669` |  | `/// <exception cref="T:System.NotSupportedException">The stream does not support seeking. </exception>`
|  | `3670` |  | `/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exceptio`
|  | `3671` |  | `public override long Position {`
|  | `3672` |  | `get { return readPos_ - start_; }`
|  | `3673` |  | `set {`
|  | `3674` |  | `long newPos = start_ + value;`
|  | `3675` |  | ``
|  | `3676` |  | `if (newPos < start_) {`
|  | `3677` |  | `throw new ArgumentException("Negative position is invalid");`
|  | `3678` |  | `}`
|  | `3679` |  | ``
|  | `3680` |  | `if (newPos >= end_) {`
|  | `3681` |  | `throw new InvalidOperationException("Cannot seek past end");`
|  | `3682` |  | `}`
|  | `3683` |  | `readPos_ = newPos;`
|  | `3684` |  | `}`
|  | `3685` |  | `}`
|  | `3686` |  | ``
|  | `3687` |  | `/// <summary>`
|  | `3688` |  | `/// Gets the length in bytes of the stream.`
|  | `3689` |  | `/// </summary>`
|  | `3690` |  | `/// <value></value>`
|  | `3691` |  | `/// <returns>A long value representing the length of the stream in bytes.</returns>`
|  | `3692` |  | `/// <exception cref="T:System.NotSupportedException">A class derived from Stream does not support seeking. </excep`
|  | `3693` |  | `/// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exceptio`
|  | `3694` |  | `public override long Length {`
|  | `3695` |  | `get { return length_; }`
|  | `3696` |  | `}`
|  | `3697` |  | ``
|  | `3698` |  | `/// <summary>`
|  | `3699` |  | `/// Gets a value indicating whether the current stream supports writing.`
|  | `3700` |  | `/// </summary>`
|  | `3701` |  | `/// <value>false</value>`
|  | `3702` |  | `/// <returns>true if the stream supports writing; otherwise, false.</returns>`
|  | `3703` |  | `public override bool CanWrite {`
|  | `3704` |  | `get { return false; }`
|  | `3705` |  | `}`
|  | `3706` |  | ``
|  | `3707` |  | `/// <summary>`
|  | `3708` |  | `/// Gets a value indicating whether the current stream supports seeking.`
|  | `3709` |  | `/// </summary>`
|  | `3710` |  | `/// <value>true</value>`
|  | `3711` |  | `/// <returns>true if the stream supports seeking; otherwise, false.</returns>`
|  | `3712` |  | `public override bool CanSeek {`
|  | `3713` |  | `get { return true; }`
|  | `3714` |  | `}`
|  | `3715` |  | ``
|  | `3716` |  | `/// <summary>`
|  | `3717` |  | `/// Gets a value indicating whether the current stream supports reading.`
|  | `3718` |  | `/// </summary>`
|  | `3719` |  | `/// <value>true.</value>`
|  | `3720` |  | `/// <returns>true if the stream supports reading; otherwise, false.</returns>`
|  | `3721` |  | `public override bool CanRead {`
|  | `3722` |  | `get { return true; }`
|  | `3723` |  | `}`
|  | `3724` |  | ``
|  | `3725` |  | `/// <summary>`
|  | `3726` |  | `/// Gets a value that determines whether the current stream can time out.`
|  | `3727` |  | `/// </summary>`
|  | `3728` |  | `/// <value></value>`
|  | `3729` |  | `/// <returns>A value that determines whether the current stream can time out.</returns>`
|  | `3730` |  | `public override bool CanTimeout {`
|  | `3731` |  | `get { return baseStream_.CanTimeout; }`
|  | `3732` |  | `}`
|  | `3733` |  | `#region Instance Fields`
|  | `3734` |  | `ZipFile zipFile_;`
|  | `3735` |  | `Stream baseStream_;`
|  | `3736` |  | `long start_;`
|  | `3737` |  | `long length_;`
|  | `3738` |  | `long readPos_;`
|  | `3739` |  | `long end_;`
|  | `3740` |  | `#endregion`
|  | `3741` |  | `}`
|  | `3742` |  | `#endregion`
|  | `3743` |  | `}`
|  | `3744` |  | ``
|  | `3745` |  | `#endregion`
|  | `3746` |  | ``
|  | `3747` |  | `#region DataSources`
|  | `3748` |  | `/// <summary>`
|  | `3749` |  | `/// Provides a static way to obtain a source of data for an entry.`
|  | `3750` |  | `/// </summary>`
|  | `3751` |  | `public interface IStaticDataSource`
|  | `3752` |  | `{`
|  | `3753` |  | `/// <summary>`
|  | `3754` |  | `/// Get a source of data by creating a new stream.`
|  | `3755` |  | `/// </summary>`
|  | `3756` |  | `/// <returns>Returns a <see cref="Stream"/> to use for compression input.</returns>`
|  | `3757` |  | `/// <remarks>Ideally a new stream is created and opened to achieve this, to avoid locking problems.</remarks>`
|  | `3758` |  | `Stream GetSource();`
|  | `3759` |  | `}`
|  | `3760` |  | ``
|  | `3761` |  | `/// <summary>`
|  | `3762` |  | `/// Represents a source of data that can dynamically provide`
|  | `3763` |  | `/// multiple <see cref="Stream">data sources</see> based on the parameters passed.`
|  | `3764` |  | `/// </summary>`
|  | `3765` |  | `public interface IDynamicDataSource`
|  | `3766` |  | `{`
|  | `3767` |  | `/// <summary>`
|  | `3768` |  | `/// Get a data source.`
|  | `3769` |  | `/// </summary>`
|  | `3770` |  | `/// <param name="entry">The <see cref="ZipEntry"/> to get a source for.</param>`
|  | `3771` |  | `/// <param name="name">The name for data if known.</param>`
|  | `3772` |  | `/// <returns>Returns a <see cref="Stream"/> to use for compression input.</returns>`
|  | `3773` |  | `/// <remarks>Ideally a new stream is created and opened to achieve this, to avoid locking problems.</remarks>`
|  | `3774` |  | `Stream GetSource(ZipEntry entry, string name);`
|  | `3775` |  | `}`
|  | `3776` |  | ``
|  | `3777` |  | `/// <summary>`
|  | `3778` |  | `/// Default implementation of a <see cref="IStaticDataSource"/> for use with files stored on disk.`
|  | `3779` |  | `/// </summary>`
|  | `3780` |  | `public class StaticDiskDataSource : IStaticDataSource`
|  | `3781` |  | `{`
|  | `3782` |  | `/// <summary>`
|  | `3783` |  | `/// Initialise a new instnace of <see cref="StaticDiskDataSource"/>`
|  | `3784` |  | `/// </summary>`
|  | `3785` |  | `/// <param name="fileName">The name of the file to obtain data from.</param>`
|  | `3786` |  | `public StaticDiskDataSource(string fileName)`
|  | `3787` |  | `{`
|  | `3788` |  | `fileName_ = fileName;`
|  | `3789` |  | `}`
|  | `3790` |  | ``
|  | `3791` |  | `#region IDataSource Members`
|  | `3792` |  | ``
|  | `3793` |  | `/// <summary>`
|  | `3794` |  | `/// Get a <see cref="Stream"/> providing data.`
|  | `3795` |  | `/// </summary>`
|  | `3796` |  | `/// <returns>Returns a <see cref="Stream"/> provising data.</returns>`
|  | `3797` |  | `public Stream GetSource()`
|  | `3798` |  | `{`
|  | `3799` |  | `return File.Open(fileName_, FileMode.Open, FileAccess.Read, FileShare.Read);`
|  | `3800` |  | `}`
|  | `3801` |  | ``
|  | `3802` |  | `readonly`
|  | `3803` |  | ``
|  | `3804` |  | `#endregion`
|  | `3805` |  | `#region Instance Fields`
|  | `3806` |  | `string fileName_;`
|  | `3807` |  | `#endregion`
|  | `3808` |  | `}`
|  | `3809` |  | ``
|  | `3810` |  | ``
|  | `3811` |  | `/// <summary>`
|  | `3812` |  | `/// Default implementation of <see cref="IDynamicDataSource"/> for files stored on disk.`
|  | `3813` |  | `/// </summary>`
|  | `3814` |  | `public class DynamicDiskDataSource : IDynamicDataSource`
|  | `3815` |  | `{`
|  | `3816` |  | ``
|  | `3817` |  | `#region IDataSource Members`
|  | `3818` |  | `/// <summary>`
|  | `3819` |  | `/// Get a <see cref="Stream"/> providing data for an entry.`
|  | `3820` |  | `/// </summary>`
|  | `3821` |  | `/// <param name="entry">The entry to provide data for.</param>`
|  | `3822` |  | `/// <param name="name">The file name for data if known.</param>`
|  | `3823` |  | `/// <returns>Returns a stream providing data; or null if not available</returns>`
|  | `3824` |  | `public Stream GetSource(ZipEntry entry, string name)`
|  | `3825` |  | `{`
| 65540 | `3826` |  | `Stream result = null;`
|  | `3827` |  | ``
| 65540 | `3828` |  | `if (name != null) {`
| 3 | `3829` |  | `result = File.Open(name, FileMode.Open, FileAccess.Read, FileShare.Read);`
|  | `3830` |  | `}`
|  | `3831` |  | ``
| 65540 | `3832` |  | `return result;`
|  | `3833` |  | `}`
|  | `3834` |  | ``
|  | `3835` |  | `#endregion`
|  | `3836` |  | `}`
|  | `3837` |  | ``
|  | `3838` |  | `#endregion`
|  | `3839` |  | ``
|  | `3840` |  | `#region Archive Storage`
|  | `3841` |  | `/// <summary>`
|  | `3842` |  | `/// Defines facilities for data storage when updating Zip Archives.`
|  | `3843` |  | `/// </summary>`
|  | `3844` |  | `public interface IArchiveStorage`
|  | `3845` |  | `{`
|  | `3846` |  | `/// <summary>`
|  | `3847` |  | `/// Get the <see cref="FileUpdateMode"/> to apply during updates.`
|  | `3848` |  | `/// </summary>`
|  | `3849` |  | `FileUpdateMode UpdateMode { get; }`
|  | `3850` |  | ``
|  | `3851` |  | `/// <summary>`
|  | `3852` |  | `/// Get an empty <see cref="Stream"/> that can be used for temporary output.`
|  | `3853` |  | `/// </summary>`
|  | `3854` |  | `/// <returns>Returns a temporary output <see cref="Stream"/></returns>`
|  | `3855` |  | `/// <seealso cref="ConvertTemporaryToFinal"></seealso>`
|  | `3856` |  | `Stream GetTemporaryOutput();`
|  | `3857` |  | ``
|  | `3858` |  | `/// <summary>`
|  | `3859` |  | `/// Convert a temporary output stream to a final stream.`
|  | `3860` |  | `/// </summary>`
|  | `3861` |  | `/// <returns>The resulting final <see cref="Stream"/></returns>`
|  | `3862` |  | `/// <seealso cref="GetTemporaryOutput"/>`
|  | `3863` |  | `Stream ConvertTemporaryToFinal();`
|  | `3864` |  | ``
|  | `3865` |  | `/// <summary>`
|  | `3866` |  | `/// Make a temporary copy of the original stream.`
|  | `3867` |  | `/// </summary>`
|  | `3868` |  | `/// <param name="stream">The <see cref="Stream"/> to copy.</param>`
|  | `3869` |  | `/// <returns>Returns a temporary output <see cref="Stream"/> that is a copy of the input.</returns>`
|  | `3870` |  | `Stream MakeTemporaryCopy(Stream stream);`
|  | `3871` |  | ``
|  | `3872` |  | `/// <summary>`
|  | `3873` |  | `/// Return a stream suitable for performing direct updates on the original source.`
|  | `3874` |  | `/// </summary>`
|  | `3875` |  | `/// <param name="stream">The current stream.</param>`
|  | `3876` |  | `/// <returns>Returns a stream suitable for direct updating.</returns>`
|  | `3877` |  | `/// <remarks>This may be the current stream passed.</remarks>`
|  | `3878` |  | `Stream OpenForDirectUpdate(Stream stream);`
|  | `3879` |  | ``
|  | `3880` |  | `/// <summary>`
|  | `3881` |  | `/// Dispose of this instance.`
|  | `3882` |  | `/// </summary>`
|  | `3883` |  | `void Dispose();`
|  | `3884` |  | `}`
|  | `3885` |  | ``
|  | `3886` |  | `/// <summary>`
|  | `3887` |  | `/// An abstract <see cref="IArchiveStorage"/> suitable for extension by inheritance.`
|  | `3888` |  | `/// </summary>`
|  | `3889` |  | `abstract public class BaseArchiveStorage : IArchiveStorage`
|  | `3890` |  | `{`
|  | `3891` |  | `#region Constructors`
|  | `3892` |  | `/// <summary>`
|  | `3893` |  | `/// Initializes a new instance of the <see cref="BaseArchiveStorage"/> class.`
|  | `3894` |  | `/// </summary>`
|  | `3895` |  | `/// <param name="updateMode">The update mode.</param>`
|  | `3896` |  | `protected BaseArchiveStorage(FileUpdateMode updateMode)`
|  | `3897` |  | `{`
|  | `3898` |  | `updateMode_ = updateMode;`
|  | `3899` |  | `}`
|  | `3900` |  | `#endregion`
|  | `3901` |  | ``
|  | `3902` |  | `#region IArchiveStorage Members`
|  | `3903` |  | ``
|  | `3904` |  | `/// <summary>`
|  | `3905` |  | `/// Gets a temporary output <see cref="Stream"/>`
|  | `3906` |  | `/// </summary>`
|  | `3907` |  | `/// <returns>Returns the temporary output stream.</returns>`
|  | `3908` |  | `/// <seealso cref="ConvertTemporaryToFinal"></seealso>`
|  | `3909` |  | `public abstract Stream GetTemporaryOutput();`
|  | `3910` |  | ``
|  | `3911` |  | `/// <summary>`
|  | `3912` |  | `/// Converts the temporary <see cref="Stream"/> to its final form.`
|  | `3913` |  | `/// </summary>`
|  | `3914` |  | `/// <returns>Returns a <see cref="Stream"/> that can be used to read`
|  | `3915` |  | `/// the final storage for the archive.</returns>`
|  | `3916` |  | `/// <seealso cref="GetTemporaryOutput"/>`
|  | `3917` |  | `public abstract Stream ConvertTemporaryToFinal();`
|  | `3918` |  | ``
|  | `3919` |  | `/// <summary>`
|  | `3920` |  | `/// Make a temporary copy of a <see cref="Stream"/>.`
|  | `3921` |  | `/// </summary>`
|  | `3922` |  | `/// <param name="stream">The <see cref="Stream"/> to make a copy of.</param>`
|  | `3923` |  | `/// <returns>Returns a temporary output <see cref="Stream"/> that is a copy of the input.</returns>`
|  | `3924` |  | `public abstract Stream MakeTemporaryCopy(Stream stream);`
|  | `3925` |  | ``
|  | `3926` |  | `/// <summary>`
|  | `3927` |  | `/// Return a stream suitable for performing direct updates on the original source.`
|  | `3928` |  | `/// </summary>`
|  | `3929` |  | `/// <param name="stream">The <see cref="Stream"/> to open for direct update.</param>`
|  | `3930` |  | `/// <returns>Returns a stream suitable for direct updating.</returns>`
|  | `3931` |  | `public abstract Stream OpenForDirectUpdate(Stream stream);`
|  | `3932` |  | ``
|  | `3933` |  | `/// <summary>`
|  | `3934` |  | `/// Disposes this instance.`
|  | `3935` |  | `/// </summary>`
|  | `3936` |  | `public abstract void Dispose();`
|  | `3937` |  | ``
|  | `3938` |  | `/// <summary>`
|  | `3939` |  | `/// Gets the update mode applicable.`
|  | `3940` |  | `/// </summary>`
|  | `3941` |  | `/// <value>The update mode.</value>`
|  | `3942` |  | `public FileUpdateMode UpdateMode {`
|  | `3943` |  | `get {`
|  | `3944` |  | `return updateMode_;`
|  | `3945` |  | `}`
|  | `3946` |  | `}`
|  | `3947` |  | ``
|  | `3948` |  | `#endregion`
|  | `3949` |  | ``
|  | `3950` |  | `#region Instance Fields`
|  | `3951` |  | `FileUpdateMode updateMode_;`
|  | `3952` |  | `#endregion`
|  | `3953` |  | `}`
|  | `3954` |  | ``
|  | `3955` |  | `/// <summary>`
|  | `3956` |  | `/// An <see cref="IArchiveStorage"/> implementation suitable for hard disks.`
|  | `3957` |  | `/// </summary>`
|  | `3958` |  | `public class DiskArchiveStorage : BaseArchiveStorage`
|  | `3959` |  | `{`
|  | `3960` |  | `#region Constructors`
|  | `3961` |  | `/// <summary>`
|  | `3962` |  | `/// Initializes a new instance of the <see cref="DiskArchiveStorage"/> class.`
|  | `3963` |  | `/// </summary>`
|  | `3964` |  | `/// <param name="file">The file.</param>`
|  | `3965` |  | `/// <param name="updateMode">The update mode.</param>`
|  | `3966` |  | `public DiskArchiveStorage(ZipFile file, FileUpdateMode updateMode)`
|  | `3967` |  | `: base(updateMode)`
|  | `3968` |  | `{`
|  | `3969` |  | `if (file.Name == null) {`
|  | `3970` |  | `throw new ZipException("Cant handle non file archives");`
|  | `3971` |  | `}`
|  | `3972` |  | ``
|  | `3973` |  | `fileName_ = file.Name;`
|  | `3974` |  | `}`
|  | `3975` |  | ``
|  | `3976` |  | `/// <summary>`
|  | `3977` |  | `/// Initializes a new instance of the <see cref="DiskArchiveStorage"/> class.`
|  | `3978` |  | `/// </summary>`
|  | `3979` |  | `/// <param name="file">The file.</param>`
|  | `3980` |  | `public DiskArchiveStorage(ZipFile file)`
|  | `3981` |  | `: this(file, FileUpdateMode.Safe)`
|  | `3982` |  | `{`
|  | `3983` |  | `}`
|  | `3984` |  | `#endregion`
|  | `3985` |  | ``
|  | `3986` |  | `#region IArchiveStorage Members`
|  | `3987` |  | ``
|  | `3988` |  | `/// <summary>`
|  | `3989` |  | `/// Gets a temporary output <see cref="Stream"/> for performing updates on.`
|  | `3990` |  | `/// </summary>`
|  | `3991` |  | `/// <returns>Returns the temporary output stream.</returns>`
|  | `3992` |  | `public override Stream GetTemporaryOutput()`
|  | `3993` |  | `{`
|  | `3994` |  | `if (temporaryName_ != null) {`
|  | `3995` |  | `temporaryName_ = GetTempFileName(temporaryName_, true);`
|  | `3996` |  | `temporaryStream_ = File.Open(temporaryName_, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);`
|  | `3997` |  | `} else {`
|  | `3998` |  | `// Determine where to place files based on internal strategy.`
|  | `3999` |  | `// Currently this is always done in system temp directory.`
|  | `4000` |  | `temporaryName_ = Path.GetTempFileName();`
|  | `4001` |  | `temporaryStream_ = File.Open(temporaryName_, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);`
|  | `4002` |  | `}`
|  | `4003` |  | ``
|  | `4004` |  | `return temporaryStream_;`
|  | `4005` |  | `}`
|  | `4006` |  | ``
|  | `4007` |  | `/// <summary>`
|  | `4008` |  | `/// Converts a temporary <see cref="Stream"/> to its final form.`
|  | `4009` |  | `/// </summary>`
|  | `4010` |  | `/// <returns>Returns a <see cref="Stream"/> that can be used to read`
|  | `4011` |  | `/// the final storage for the archive.</returns>`
|  | `4012` |  | `public override Stream ConvertTemporaryToFinal()`
|  | `4013` |  | `{`
|  | `4014` |  | `if (temporaryStream_ == null) {`
|  | `4015` |  | `throw new ZipException("No temporary stream has been created");`
|  | `4016` |  | `}`
|  | `4017` |  | ``
|  | `4018` |  | `Stream result = null;`
|  | `4019` |  | ``
|  | `4020` |  | `string moveTempName = GetTempFileName(fileName_, false);`
|  | `4021` |  | `bool newFileCreated = false;`
|  | `4022` |  | ``
|  | `4023` |  | `try {`
|  | `4024` |  | `temporaryStream_.Close();`
|  | `4025` |  | `File.Move(fileName_, moveTempName);`
|  | `4026` |  | `File.Move(temporaryName_, fileName_);`
|  | `4027` |  | `newFileCreated = true;`
|  | `4028` |  | `File.Delete(moveTempName);`
|  | `4029` |  | ``
|  | `4030` |  | `result = File.Open(fileName_, FileMode.Open, FileAccess.Read, FileShare.Read);`
|  | `4031` |  | `} catch (Exception) {`
|  | `4032` |  | `result = null;`
|  | `4033` |  | ``
|  | `4034` |  | `// Try to roll back changes...`
|  | `4035` |  | `if (!newFileCreated) {`
|  | `4036` |  | `File.Move(moveTempName, fileName_);`
|  | `4037` |  | `File.Delete(temporaryName_);`
|  | `4038` |  | `}`
|  | `4039` |  | ``
|  | `4040` |  | `throw;`
|  | `4041` |  | `}`
|  | `4042` |  | ``
|  | `4043` |  | `return result;`
|  | `4044` |  | `}`
|  | `4045` |  | ``
|  | `4046` |  | `/// <summary>`
|  | `4047` |  | `/// Make a temporary copy of a stream.`
|  | `4048` |  | `/// </summary>`
|  | `4049` |  | `/// <param name="stream">The <see cref="Stream"/> to copy.</param>`
|  | `4050` |  | `/// <returns>Returns a temporary output <see cref="Stream"/> that is a copy of the input.</returns>`
|  | `4051` |  | `public override Stream MakeTemporaryCopy(Stream stream)`
|  | `4052` |  | `{`
|  | `4053` |  | `stream.Close();`
|  | `4054` |  | ``
|  | `4055` |  | `temporaryName_ = GetTempFileName(fileName_, true);`
|  | `4056` |  | `File.Copy(fileName_, temporaryName_, true);`
|  | `4057` |  | ``
|  | `4058` |  | `temporaryStream_ = new FileStream(temporaryName_,`
|  | `4059` |  | `FileMode.Open,`
|  | `4060` |  | `FileAccess.ReadWrite);`
|  | `4061` |  | `return temporaryStream_;`
|  | `4062` |  | `}`
|  | `4063` |  | ``
|  | `4064` |  | `/// <summary>`
|  | `4065` |  | `/// Return a stream suitable for performing direct updates on the original source.`
|  | `4066` |  | `/// </summary>`
|  | `4067` |  | `/// <param name="stream">The current stream.</param>`
|  | `4068` |  | `/// <returns>Returns a stream suitable for direct updating.</returns>`
|  | `4069` |  | `/// <remarks>If the <paramref name="stream"/> is not null this is used as is.</remarks>`
|  | `4070` |  | `public override Stream OpenForDirectUpdate(Stream stream)`
|  | `4071` |  | `{`
|  | `4072` |  | `Stream result;`
|  | `4073` |  | `if ((stream == null) || !stream.CanWrite) {`
|  | `4074` |  | `if (stream != null) {`
|  | `4075` |  | `stream.Close();`
|  | `4076` |  | `}`
|  | `4077` |  | ``
|  | `4078` |  | `result = new FileStream(fileName_,`
|  | `4079` |  | `FileMode.Open,`
|  | `4080` |  | `FileAccess.ReadWrite);`
|  | `4081` |  | `} else {`
|  | `4082` |  | `result = stream;`
|  | `4083` |  | `}`
|  | `4084` |  | ``
|  | `4085` |  | `return result;`
|  | `4086` |  | `}`
|  | `4087` |  | ``
|  | `4088` |  | `/// <summary>`
|  | `4089` |  | `/// Disposes this instance.`
|  | `4090` |  | `/// </summary>`
|  | `4091` |  | `public override void Dispose()`
|  | `4092` |  | `{`
|  | `4093` |  | `if (temporaryStream_ != null) {`
|  | `4094` |  | `temporaryStream_.Close();`
|  | `4095` |  | `}`
|  | `4096` |  | `}`
|  | `4097` |  | ``
|  | `4098` |  | `#endregion`
|  | `4099` |  | ``
|  | `4100` |  | `#region Internal routines`
|  | `4101` |  | `static string GetTempFileName(string original, bool makeTempFile)`
|  | `4102` |  | `{`
|  | `4103` |  | `string result = null;`
|  | `4104` |  | ``
|  | `4105` |  | `if (original == null) {`
|  | `4106` |  | `result = Path.GetTempFileName();`
|  | `4107` |  | `} else {`
|  | `4108` |  | `int counter = 0;`
|  | `4109` |  | `int suffixSeed = DateTime.Now.Second;`
|  | `4110` |  | ``
|  | `4111` |  | `while (result == null) {`
|  | `4112` |  | `counter += 1;`
|  | `4113` |  | `string newName = string.Format("{0}.{1}{2}.tmp", original, suffixSeed, counter);`
|  | `4114` |  | `if (!File.Exists(newName)) {`
|  | `4115` |  | `if (makeTempFile) {`
|  | `4116` |  | `try {`
|  | `4117` |  | `// Try and create the file.`
|  | `4118` |  | `using (FileStream stream = File.Create(newName)) {`
|  | `4119` |  | `}`
|  | `4120` |  | `result = newName;`
|  | `4121` |  | `} catch {`
|  | `4122` |  | `suffixSeed = DateTime.Now.Second;`
|  | `4123` |  | `}`
|  | `4124` |  | `} else {`
|  | `4125` |  | `result = newName;`
|  | `4126` |  | `}`
|  | `4127` |  | `}`
|  | `4128` |  | `}`
|  | `4129` |  | `}`
|  | `4130` |  | `return result;`
|  | `4131` |  | `}`
|  | `4132` |  | `#endregion`
|  | `4133` |  | ``
|  | `4134` |  | `#region Instance Fields`
|  | `4135` |  | `Stream temporaryStream_;`
|  | `4136` |  | `string fileName_;`
|  | `4137` |  | `string temporaryName_;`
|  | `4138` |  | `#endregion`
|  | `4139` |  | `}`
|  | `4140` |  | ``
|  | `4141` |  | `/// <summary>`
|  | `4142` |  | `/// An <see cref="IArchiveStorage"/> implementation suitable for in memory streams.`
|  | `4143` |  | `/// </summary>`
|  | `4144` |  | `public class MemoryArchiveStorage : BaseArchiveStorage`
|  | `4145` |  | `{`
|  | `4146` |  | `#region Constructors`
|  | `4147` |  | `/// <summary>`
|  | `4148` |  | `/// Initializes a new instance of the <see cref="MemoryArchiveStorage"/> class.`
|  | `4149` |  | `/// </summary>`
|  | `4150` |  | `public MemoryArchiveStorage()`
|  | `4151` |  | `: base(FileUpdateMode.Direct)`
|  | `4152` |  | `{`
|  | `4153` |  | `}`
|  | `4154` |  | ``
|  | `4155` |  | `/// <summary>`
|  | `4156` |  | `/// Initializes a new instance of the <see cref="MemoryArchiveStorage"/> class.`
|  | `4157` |  | `/// </summary>`
|  | `4158` |  | `/// <param name="updateMode">The <see cref="FileUpdateMode"/> to use</param>`
|  | `4159` |  | `/// <remarks>This constructor is for testing as memory streams dont really require safe mode.</remarks>`
|  | `4160` |  | `public MemoryArchiveStorage(FileUpdateMode updateMode)`
|  | `4161` |  | `: base(updateMode)`
|  | `4162` |  | `{`
|  | `4163` |  | `}`
|  | `4164` |  | ``
|  | `4165` |  | `#endregion`
|  | `4166` |  | ``
|  | `4167` |  | `#region Properties`
|  | `4168` |  | `/// <summary>`
|  | `4169` |  | `/// Get the stream returned by <see cref="ConvertTemporaryToFinal"/> if this was in fact called.`
|  | `4170` |  | `/// </summary>`
|  | `4171` |  | `public MemoryStream FinalStream {`
|  | `4172` |  | `get { return finalStream_; }`
|  | `4173` |  | `}`
|  | `4174` |  | ``
|  | `4175` |  | `#endregion`
|  | `4176` |  | ``
|  | `4177` |  | `#region IArchiveStorage Members`
|  | `4178` |  | ``
|  | `4179` |  | `/// <summary>`
|  | `4180` |  | `/// Gets the temporary output <see cref="Stream"/>`
|  | `4181` |  | `/// </summary>`
|  | `4182` |  | `/// <returns>Returns the temporary output stream.</returns>`
|  | `4183` |  | `public override Stream GetTemporaryOutput()`
|  | `4184` |  | `{`
|  | `4185` |  | `temporaryStream_ = new MemoryStream();`
|  | `4186` |  | `return temporaryStream_;`
|  | `4187` |  | `}`
|  | `4188` |  | ``
|  | `4189` |  | `/// <summary>`
|  | `4190` |  | `/// Converts the temporary <see cref="Stream"/> to its final form.`
|  | `4191` |  | `/// </summary>`
|  | `4192` |  | `/// <returns>Returns a <see cref="Stream"/> that can be used to read`
|  | `4193` |  | `/// the final storage for the archive.</returns>`
|  | `4194` |  | `public override Stream ConvertTemporaryToFinal()`
|  | `4195` |  | `{`
|  | `4196` |  | `if (temporaryStream_ == null) {`
|  | `4197` |  | `throw new ZipException("No temporary stream has been created");`
|  | `4198` |  | `}`
|  | `4199` |  | ``
|  | `4200` |  | `finalStream_ = new MemoryStream(temporaryStream_.ToArray());`
|  | `4201` |  | `return finalStream_;`
|  | `4202` |  | `}`
|  | `4203` |  | ``
|  | `4204` |  | `/// <summary>`
|  | `4205` |  | `/// Make a temporary copy of the original stream.`
|  | `4206` |  | `/// </summary>`
|  | `4207` |  | `/// <param name="stream">The <see cref="Stream"/> to copy.</param>`
|  | `4208` |  | `/// <returns>Returns a temporary output <see cref="Stream"/> that is a copy of the input.</returns>`
|  | `4209` |  | `public override Stream MakeTemporaryCopy(Stream stream)`
|  | `4210` |  | `{`
|  | `4211` |  | `temporaryStream_ = new MemoryStream();`
|  | `4212` |  | `stream.Position = 0;`
|  | `4213` |  | `StreamUtils.Copy(stream, temporaryStream_, new byte[4096]);`
|  | `4214` |  | `return temporaryStream_;`
|  | `4215` |  | `}`
|  | `4216` |  | ``
|  | `4217` |  | `/// <summary>`
|  | `4218` |  | `/// Return a stream suitable for performing direct updates on the original source.`
|  | `4219` |  | `/// </summary>`
|  | `4220` |  | `/// <param name="stream">The original source stream</param>`
|  | `4221` |  | `/// <returns>Returns a stream suitable for direct updating.</returns>`
|  | `4222` |  | `/// <remarks>If the <paramref name="stream"/> passed is not null this is used;`
|  | `4223` |  | `/// otherwise a new <see cref="MemoryStream"/> is returned.</remarks>`
|  | `4224` |  | `public override Stream OpenForDirectUpdate(Stream stream)`
|  | `4225` |  | `{`
|  | `4226` |  | `Stream result;`
|  | `4227` |  | `if ((stream == null) || !stream.CanWrite) {`
|  | `4228` |  | ``
|  | `4229` |  | `result = new MemoryStream();`
|  | `4230` |  | ``
|  | `4231` |  | `if (stream != null) {`
|  | `4232` |  | `stream.Position = 0;`
|  | `4233` |  | `StreamUtils.Copy(stream, result, new byte[4096]);`
|  | `4234` |  | ``
|  | `4235` |  | `stream.Close();`
|  | `4236` |  | `}`
|  | `4237` |  | `} else {`
|  | `4238` |  | `result = stream;`
|  | `4239` |  | `}`
|  | `4240` |  | ``
|  | `4241` |  | `return result;`
|  | `4242` |  | `}`
|  | `4243` |  | ``
|  | `4244` |  | `/// <summary>`
|  | `4245` |  | `/// Disposes this instance.`
|  | `4246` |  | `/// </summary>`
|  | `4247` |  | `public override void Dispose()`
|  | `4248` |  | `{`
|  | `4249` |  | `if (temporaryStream_ != null) {`
|  | `4250` |  | `temporaryStream_.Close();`
|  | `4251` |  | `}`
|  | `4252` |  | `}`
|  | `4253` |  | ``
|  | `4254` |  | `#endregion`
|  | `4255` |  | ``
|  | `4256` |  | `#region Instance Fields`
|  | `4257` |  | `MemoryStream temporaryStream_;`
|  | `4258` |  | `MemoryStream finalStream_;`
|  | `4259` |  | `#endregion`
|  | `4260` |  | `}`
|  | `4261` |  | ``
|  | `4262` |  | `#endregion`
|  | `4263` |  | `}`