2048 lines
86 KiB
C#
2048 lines
86 KiB
C#
/* This file is part of SevenZipSharp.
|
|
|
|
SevenZipSharp is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
SevenZipSharp is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with SevenZipSharp. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
#if DOTNET20
|
|
using System.Threading;
|
|
#else
|
|
using System.Linq;
|
|
#endif
|
|
using System.Runtime.InteropServices;
|
|
#if !WINCE
|
|
using System.Security.Permissions;
|
|
#endif
|
|
using SevenZip.Sdk;
|
|
using SevenZip.Sdk.Compression.Lzma;
|
|
#if MONO
|
|
using SevenZip.Mono.COM;
|
|
#endif
|
|
|
|
namespace SevenZip
|
|
{
|
|
#if COMPRESS
|
|
/// <summary>
|
|
/// Class for packing files into 7-zip archives
|
|
/// </summary>
|
|
public sealed partial class SevenZipCompressor
|
|
#if UNMANAGED
|
|
: SevenZipBase
|
|
#endif
|
|
{
|
|
#if UNMANAGED
|
|
#region Fields
|
|
private bool _compressingFilesOnDisk;
|
|
/// <summary>
|
|
/// Gets or sets the archiving compression level.
|
|
/// </summary>
|
|
public CompressionLevel CompressionLevel { get; set; }
|
|
private OutArchiveFormat _archiveFormat = OutArchiveFormat.SevenZip;
|
|
private CompressionMethod _compressionMethod = CompressionMethod.Default;
|
|
/// <summary>
|
|
/// Gets the custom compression parameters - for advanced users only.
|
|
/// </summary>
|
|
public Dictionary<string, string> CustomParameters { get; private set; }
|
|
private int _volumeSize;
|
|
private string _archiveName;
|
|
/// <summary>
|
|
/// Gets or sets the value indicating whether to include empty directories to archives. Default is true.
|
|
/// </summary>
|
|
public bool IncludeEmptyDirectories { get; set; }
|
|
/// <summary>
|
|
/// Gets or sets the value indicating whether to preserve the directory root for CompressDirectory.
|
|
/// </summary>
|
|
public bool PreserveDirectoryRoot { get; set; }
|
|
/// <summary>
|
|
/// Gets or sets the value indicating whether to preserve the directory structure.
|
|
/// </summary>
|
|
public bool DirectoryStructure { get; set; }
|
|
private bool _directoryCompress;
|
|
/// <summary>
|
|
/// Gets or sets the compression mode.
|
|
/// </summary>
|
|
public CompressionMode CompressionMode { get; set; }
|
|
private UpdateData _updateData;
|
|
private uint _oldFilesCount;
|
|
/// <summary>
|
|
/// Gets or sets the value indicating whether to encrypt 7-Zip archive headers.
|
|
/// </summary>
|
|
public bool EncryptHeaders { get; set; }
|
|
/// <summary>
|
|
/// Gets or sets the value indicating whether to compress files only open for writing.
|
|
/// </summary>
|
|
public bool ScanOnlyWritable { get; set; }
|
|
/// <summary>
|
|
/// Gets or sets the encryption method for zip archives.
|
|
/// </summary>
|
|
public ZipEncryptionMethod ZipEncryptionMethod { get; set; }
|
|
/// <summary>
|
|
/// Gets or sets the temporary folder path.
|
|
/// </summary>
|
|
public string TempFolderPath { get; set; }
|
|
/// <summary>
|
|
/// Gets or sets the default archive item name used when an item to be compressed has no name,
|
|
/// for example, when you compress a MemoryStream instance.
|
|
/// </summary>
|
|
public string DefaultItemName { get; set; }
|
|
/// <summary>
|
|
/// Gets or sets the value indicating whether to compress as fast as possible, without calling events.
|
|
/// </summary>
|
|
public bool FastCompression { get; set; }
|
|
#endregion
|
|
#endif
|
|
private static int _lzmaDictionarySize = 1 << 22;
|
|
|
|
#if UNMANAGED
|
|
|
|
private void CommonInit()
|
|
{
|
|
DirectoryStructure = true;
|
|
IncludeEmptyDirectories = true;
|
|
CompressionLevel = CompressionLevel.Normal;
|
|
CompressionMode = CompressionMode.Create;
|
|
ZipEncryptionMethod = ZipEncryptionMethod.ZipCrypto;
|
|
CustomParameters = new Dictionary<string, string>();
|
|
_updateData = new UpdateData();
|
|
DefaultItemName = "default";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the SevenZipCompressor class.
|
|
/// </summary>
|
|
public SevenZipCompressor()
|
|
{
|
|
try
|
|
{
|
|
#if !WINCE
|
|
TempFolderPath = Path.GetTempPath();
|
|
//TempFolderPath = Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.User);
|
|
#else
|
|
TempFolderPath = "Temp";
|
|
#endif
|
|
}
|
|
catch (System.Security.SecurityException) // Registry access is not allowed, etc.
|
|
{
|
|
/*throw new SevenZipCompressionFailedException(
|
|
"Attempted to get TEMP environment variable but registry access was not allowed (security settings on your machine). You must call SevenZipCompressor constructor overload with your own temporary path.");
|
|
*/
|
|
throw new SevenZipCompressionFailedException(
|
|
"Path.GetTempPath() threw a System.Security.SecurityException. You must call SevenZipCompressor constructor overload with your own temporary path.");
|
|
}
|
|
CommonInit();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the SevenZipCompressor class.
|
|
/// </summary>
|
|
/// <param name="temporaryPath">Your own temporary path (default is set in the parameterless constructor overload.)</param>
|
|
public SevenZipCompressor(string temporaryPath)
|
|
{
|
|
TempFolderPath = temporaryPath;
|
|
if (!Directory.Exists(TempFolderPath))
|
|
{
|
|
try
|
|
{
|
|
Directory.CreateDirectory(TempFolderPath);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
throw new SevenZipCompressionFailedException("The specified temporary path is invalid.");
|
|
}
|
|
}
|
|
CommonInit();
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Checks if the specified stream supports compression.
|
|
/// </summary>
|
|
/// <param name="stream">The stream to check.</param>
|
|
private static void ValidateStream(Stream stream)
|
|
{
|
|
if (!stream.CanWrite || !stream.CanSeek)
|
|
{
|
|
throw new ArgumentException("The specified stream can not seek or is not writable.", "stream");
|
|
}
|
|
}
|
|
|
|
#if UNMANAGED
|
|
|
|
#region Private functions
|
|
|
|
private IOutArchive MakeOutArchive(IInStream inArchiveStream)
|
|
{
|
|
IInArchive inArchive = SevenZipLibraryManager.InArchive(
|
|
Formats.InForOutFormats[_archiveFormat], this);
|
|
using (ArchiveOpenCallback openCallback = GetArchiveOpenCallback())
|
|
{
|
|
ulong checkPos = 1 << 15;
|
|
if (inArchive.Open(inArchiveStream, ref checkPos, openCallback) != (int) OperationResult.Ok)
|
|
{
|
|
if (
|
|
!ThrowException(null, new SevenZipArchiveException("Can not update the archive: Open() failed.")))
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
_oldFilesCount = inArchive.GetNumberOfItems();
|
|
}
|
|
return (IOutArchive) inArchive;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Guaranties the correct work of the SetCompressionProperties function
|
|
/// </summary>
|
|
/// <param name="method">The compression method to check</param>
|
|
/// <returns>The value indicating whether the specified method is valid for the current ArchiveFormat</returns>
|
|
private bool MethodIsValid(CompressionMethod method)
|
|
{
|
|
if (method == CompressionMethod.Default)
|
|
{
|
|
return true;
|
|
}
|
|
switch (_archiveFormat)
|
|
{
|
|
case OutArchiveFormat.Zip:
|
|
return method != CompressionMethod.Ppmd;
|
|
case OutArchiveFormat.GZip:
|
|
return method == CompressionMethod.Deflate;
|
|
case OutArchiveFormat.BZip2:
|
|
return method == CompressionMethod.BZip2;
|
|
case OutArchiveFormat.SevenZip:
|
|
return method != CompressionMethod.Deflate && method != CompressionMethod.Deflate64;
|
|
case OutArchiveFormat.Tar:
|
|
return method == CompressionMethod.Copy;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private bool SwitchIsInCustomParameters(string name)
|
|
{
|
|
return CustomParameters.ContainsKey(name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the compression properties
|
|
/// </summary>
|
|
private void SetCompressionProperties()
|
|
{
|
|
switch (_archiveFormat)
|
|
{
|
|
case OutArchiveFormat.Tar:
|
|
break;
|
|
default:
|
|
ISetProperties setter = CompressionMode == CompressionMode.Create && _updateData.FileNamesToModify == null
|
|
? (ISetProperties) SevenZipLibraryManager.OutArchive(
|
|
_archiveFormat, this)
|
|
: (ISetProperties) SevenZipLibraryManager.InArchive(
|
|
Formats.InForOutFormats[_archiveFormat], this);
|
|
if (setter == null)
|
|
{
|
|
if (!ThrowException(null,
|
|
new CompressionFailedException(
|
|
"The specified archive format is unsupported.")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if (_volumeSize > 0 && ArchiveFormat != OutArchiveFormat.SevenZip)
|
|
{
|
|
throw new CompressionFailedException("Unfortunately, the creation of multivolume non-7Zip archives is not implemented. It will be one day, though.");
|
|
}
|
|
if (CustomParameters.ContainsKey("x") || CustomParameters.ContainsKey("m"))
|
|
{
|
|
if (
|
|
!ThrowException(null,
|
|
new CompressionFailedException(
|
|
"The specified compression parameters are invalid.")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
var names = new List<IntPtr>(2 + CustomParameters.Count);
|
|
var values = new List<PropVariant>(2 + CustomParameters.Count);
|
|
#if !WINCE
|
|
var sp = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
|
|
sp.Demand();
|
|
#endif
|
|
#region Initialize compression properties
|
|
|
|
if (_compressionMethod == CompressionMethod.Default)
|
|
{
|
|
names.Add(Marshal.StringToBSTR("x"));
|
|
values.Add(new PropVariant());
|
|
foreach (var pair in CustomParameters)
|
|
{
|
|
names.Add(Marshal.StringToBSTR(pair.Key));
|
|
var pv = new PropVariant();
|
|
if (pair.Key == "fb" || pair.Key == "pass" || pair.Key == "d")
|
|
{
|
|
pv.VarType = VarEnum.VT_UI4;
|
|
pv.UInt32Value = Convert.ToUInt32(pair.Value, CultureInfo.InvariantCulture);
|
|
}
|
|
else
|
|
{
|
|
pv.VarType = VarEnum.VT_BSTR;
|
|
pv.Value = Marshal.StringToBSTR(pair.Value);
|
|
}
|
|
values.Add(pv);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
names.Add(Marshal.StringToBSTR("x"));
|
|
names.Add(_archiveFormat == OutArchiveFormat.Zip
|
|
? Marshal.StringToBSTR("m")
|
|
: Marshal.StringToBSTR("0"));
|
|
values.Add(new PropVariant());
|
|
var pv = new PropVariant
|
|
{
|
|
VarType = VarEnum.VT_BSTR,
|
|
Value = Marshal.StringToBSTR(Formats.MethodNames[_compressionMethod])
|
|
};
|
|
values.Add(pv);
|
|
foreach (var pair in CustomParameters)
|
|
{
|
|
names.Add(Marshal.StringToBSTR(pair.Key));
|
|
pv = new PropVariant();
|
|
if (pair.Key == "fb" || pair.Key == "pass" || pair.Key == "d")
|
|
{
|
|
pv.VarType = VarEnum.VT_UI4;
|
|
pv.UInt32Value = Convert.ToUInt32(pair.Value, CultureInfo.InvariantCulture);
|
|
}
|
|
else
|
|
{
|
|
pv.VarType = VarEnum.VT_BSTR;
|
|
pv.Value = Marshal.StringToBSTR(pair.Value);
|
|
}
|
|
values.Add(pv);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Set compression level
|
|
|
|
PropVariant clpv = values[0];
|
|
clpv.VarType = VarEnum.VT_UI4;
|
|
switch (CompressionLevel)
|
|
{
|
|
case CompressionLevel.None:
|
|
clpv.UInt32Value = 0;
|
|
break;
|
|
case CompressionLevel.Fast:
|
|
clpv.UInt32Value = 1;
|
|
break;
|
|
case CompressionLevel.Low:
|
|
clpv.UInt32Value = 3;
|
|
break;
|
|
case CompressionLevel.Normal:
|
|
clpv.UInt32Value = 5;
|
|
break;
|
|
case CompressionLevel.High:
|
|
clpv.UInt32Value = 7;
|
|
break;
|
|
case CompressionLevel.Ultra:
|
|
clpv.UInt32Value = 9;
|
|
break;
|
|
}
|
|
values[0] = clpv;
|
|
|
|
#endregion
|
|
|
|
#region Encrypt headers
|
|
|
|
if (EncryptHeaders && _archiveFormat == OutArchiveFormat.SevenZip &&
|
|
!SwitchIsInCustomParameters("he"))
|
|
{
|
|
names.Add(Marshal.StringToBSTR("he"));
|
|
var tmp = new PropVariant {VarType = VarEnum.VT_BSTR, Value = Marshal.StringToBSTR("on")};
|
|
values.Add(tmp);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Zip Encryption
|
|
|
|
if (_archiveFormat == OutArchiveFormat.Zip && ZipEncryptionMethod != ZipEncryptionMethod.ZipCrypto &&
|
|
!SwitchIsInCustomParameters("em"))
|
|
{
|
|
names.Add(Marshal.StringToBSTR("em"));
|
|
var tmp = new PropVariant
|
|
{
|
|
VarType = VarEnum.VT_BSTR,
|
|
Value = Marshal.StringToBSTR(
|
|
#if !WINCE
|
|
Enum.GetName(typeof (ZipEncryptionMethod), ZipEncryptionMethod))
|
|
#else
|
|
OpenNETCF.Enum2.GetName(typeof (ZipEncryptionMethod), ZipEncryptionMethod))
|
|
#endif
|
|
};
|
|
values.Add(tmp);
|
|
}
|
|
|
|
#endregion
|
|
|
|
var namesHandle = GCHandle.Alloc(names.ToArray(), GCHandleType.Pinned);
|
|
var valuesHandle = GCHandle.Alloc(values.ToArray(), GCHandleType.Pinned);
|
|
try
|
|
{
|
|
if (setter != null) //ReSharper
|
|
setter.SetProperties(namesHandle.AddrOfPinnedObject(), valuesHandle.AddrOfPinnedObject(),
|
|
names.Count);
|
|
}
|
|
finally
|
|
{
|
|
namesHandle.Free();
|
|
valuesHandle.Free();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the common root of file names
|
|
/// </summary>
|
|
/// <param name="files">Array of file names</param>
|
|
/// <returns>Common root</returns>
|
|
private static int CommonRoot(ICollection<string> files)
|
|
{
|
|
var splittedFileNames = new List<string[]>(files.Count);
|
|
#if CS4
|
|
splittedFileNames.AddRange(files.Select(fn => fn.Split(Path.DirectorySeparatorChar)));
|
|
#else
|
|
foreach (string fn in files)
|
|
{
|
|
splittedFileNames.Add(fn.Split(Path.DirectorySeparatorChar));
|
|
}
|
|
#endif
|
|
int minSplitLength = splittedFileNames[0].Length - 1;
|
|
if (files.Count > 1)
|
|
{
|
|
for (int i = 1; i < files.Count; i++)
|
|
{
|
|
if (minSplitLength > splittedFileNames[i].Length)
|
|
{
|
|
minSplitLength = splittedFileNames[i].Length;
|
|
}
|
|
}
|
|
}
|
|
string res = "";
|
|
for (int i = 0; i < minSplitLength; i++)
|
|
{
|
|
bool common = true;
|
|
for (int j = 1; j < files.Count; j++)
|
|
{
|
|
if (!(common &= splittedFileNames[j - 1][i] == splittedFileNames[j][i]))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (common)
|
|
{
|
|
res += splittedFileNames[0][i] + Path.DirectorySeparatorChar;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return res.Length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validates the common root
|
|
/// </summary>
|
|
/// <param name="commonRootLength">The length of the common root of the file names.</param>
|
|
/// <param name="files">Array of file names</param>
|
|
private static void CheckCommonRoot(string[] files, ref int commonRootLength)
|
|
{
|
|
string commonRoot;
|
|
try
|
|
{
|
|
commonRoot = files[0].Substring(0, commonRootLength);
|
|
}
|
|
catch (ArgumentOutOfRangeException)
|
|
{
|
|
throw new SevenZipInvalidFileNamesException("invalid common root.");
|
|
}
|
|
if (commonRoot.EndsWith(new string(Path.DirectorySeparatorChar, 1), StringComparison.CurrentCulture))
|
|
{
|
|
commonRoot = commonRoot.Substring(0, commonRootLength - 1);
|
|
commonRootLength--;
|
|
}
|
|
#if CS4
|
|
if (files.Any(fn => !fn.StartsWith(commonRoot, StringComparison.CurrentCulture)))
|
|
{
|
|
throw new SevenZipInvalidFileNamesException("invalid common root.");
|
|
}
|
|
#else
|
|
foreach (string fn in files)
|
|
{
|
|
if (!fn.StartsWith(commonRoot, StringComparison.CurrentCulture))
|
|
{
|
|
throw new SevenZipInvalidFileNamesException("invalid common root.");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures that directory directory is not empty
|
|
/// </summary>
|
|
/// <param name="directory">Directory name</param>
|
|
/// <returns>False if is not empty</returns>
|
|
private static bool RecursiveDirectoryEmptyCheck(string directory)
|
|
{
|
|
var di = new DirectoryInfo(directory);
|
|
if (di.GetFiles().Length > 0)
|
|
{
|
|
return false;
|
|
}
|
|
bool empty = true;
|
|
foreach (DirectoryInfo cdi in di.GetDirectories())
|
|
{
|
|
empty &= RecursiveDirectoryEmptyCheck(cdi.FullName);
|
|
if (!empty)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Makes special FileInfo array for the archive file table.
|
|
/// </summary>
|
|
/// <param name="files">Array of files to pack.</param>
|
|
/// <param name="commonRootLength">The length of the common root of file names</param>
|
|
/// <param name="directoryCompress">The value indicating whether to produce the array for files in a particular directory or just for an array of files.</param>
|
|
/// <param name="directoryStructure">Preserve directory structure.</param>
|
|
/// <returns>Special FileInfo array for the archive file table.</returns>
|
|
private static FileInfo[] ProduceFileInfoArray(
|
|
string[] files, int commonRootLength,
|
|
bool directoryCompress, bool directoryStructure)
|
|
{
|
|
var fis = new List<FileInfo>(files.Length);
|
|
string commonRoot = files[0].Substring(0, commonRootLength);
|
|
if (directoryCompress)
|
|
{
|
|
#if CS4
|
|
fis.AddRange(files.Select(fn => new FileInfo(fn)));
|
|
#else
|
|
foreach (string fn in files)
|
|
{
|
|
fis.Add(new FileInfo(fn));
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (!directoryStructure)
|
|
{
|
|
#if CS4
|
|
fis.AddRange(from fn in files where !Directory.Exists(fn) select new FileInfo(fn));
|
|
#else
|
|
foreach (string fn in files)
|
|
{
|
|
if (!Directory.Exists(fn))
|
|
{
|
|
fis.Add(new FileInfo(fn));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
var fns = new List<string>(files.Length);
|
|
CheckCommonRoot(files, ref commonRootLength);
|
|
if (commonRootLength > 0)
|
|
{
|
|
commonRootLength++;
|
|
foreach (string f in files)
|
|
{
|
|
string[] splittedAfn = f.Substring(commonRootLength).Split(Path.DirectorySeparatorChar);
|
|
string cfn = commonRoot;
|
|
foreach (string t in splittedAfn) {
|
|
cfn += Path.DirectorySeparatorChar + t;
|
|
if (!fns.Contains(cfn))
|
|
{
|
|
fis.Add(new FileInfo(cfn));
|
|
fns.Add(cfn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (string f in files)
|
|
{
|
|
string[] splittedAfn = f.Substring(commonRootLength).Split(Path.DirectorySeparatorChar);
|
|
string cfn = splittedAfn[0];
|
|
for (int i = 1; i < splittedAfn.Length; i++)
|
|
{
|
|
cfn += Path.DirectorySeparatorChar + splittedAfn[i];
|
|
if (!fns.Contains(cfn))
|
|
{
|
|
fis.Add(new FileInfo(cfn));
|
|
fns.Add(cfn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return fis.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recursive function for adding files in directory
|
|
/// </summary>
|
|
/// <param name="directory">Directory directory</param>
|
|
/// <param name="files">List of files</param>
|
|
/// <param name="searchPattern">Search string, such as "*.txt"</param>
|
|
private void AddFilesFromDirectory(string directory, ICollection<string> files, string searchPattern)
|
|
{
|
|
var di = new DirectoryInfo(directory);
|
|
foreach (FileInfo fi in di.GetFiles(searchPattern))
|
|
{
|
|
if (!ScanOnlyWritable)
|
|
{
|
|
files.Add(fi.FullName);
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
using (fi.OpenWrite()) {}
|
|
files.Add(fi.FullName);
|
|
}
|
|
catch (IOException) {}
|
|
}
|
|
}
|
|
foreach (DirectoryInfo cdi in di.GetDirectories())
|
|
{
|
|
if (IncludeEmptyDirectories)
|
|
{
|
|
files.Add(cdi.FullName);
|
|
}
|
|
AddFilesFromDirectory(cdi.FullName, files, searchPattern);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetArchiveUpdateCallback overloads
|
|
|
|
/// <summary>
|
|
/// Performs the common ArchiveUpdateCallback initialization.
|
|
/// </summary>
|
|
/// <param name="auc">The ArchiveUpdateCallback instance to initialize.</param>
|
|
private void CommonUpdateCallbackInit(ArchiveUpdateCallback auc)
|
|
{
|
|
auc.FileCompressionStarted += FileCompressionStartedEventProxy;
|
|
auc.Compressing += CompressingEventProxy;
|
|
auc.FileCompressionFinished += FileCompressionFinishedEventProxy;
|
|
auc.DefaultItemName = DefaultItemName;
|
|
auc.FastCompression = FastCompression;
|
|
}
|
|
|
|
private float GetDictionarySize()
|
|
{
|
|
float dictionarySize = 0.001f;
|
|
switch (_compressionMethod)
|
|
{
|
|
case CompressionMethod.Default:
|
|
case CompressionMethod.Lzma:
|
|
case CompressionMethod.Lzma2:
|
|
switch (CompressionLevel)
|
|
{
|
|
case CompressionLevel.None:
|
|
dictionarySize = 0.001f;
|
|
break;
|
|
case CompressionLevel.Fast:
|
|
dictionarySize = 1.0f / 16 * 7.5f + 4;
|
|
break;
|
|
case CompressionLevel.Low:
|
|
dictionarySize = 7.5f * 11.5f + 4;
|
|
break;
|
|
case CompressionLevel.Normal:
|
|
dictionarySize = 16 * 11.5f + 4;
|
|
break;
|
|
case CompressionLevel.High:
|
|
dictionarySize = 32 * 11.5f + 4;
|
|
break;
|
|
case CompressionLevel.Ultra:
|
|
dictionarySize = 64 * 11.5f + 4;
|
|
break;
|
|
}
|
|
break;
|
|
case CompressionMethod.BZip2:
|
|
switch (CompressionLevel)
|
|
{
|
|
case CompressionLevel.None:
|
|
dictionarySize = 0;
|
|
break;
|
|
case CompressionLevel.Fast:
|
|
dictionarySize = 0.095f;
|
|
break;
|
|
case CompressionLevel.Low:
|
|
dictionarySize = 0.477f;
|
|
break;
|
|
case CompressionLevel.Normal:
|
|
case CompressionLevel.High:
|
|
case CompressionLevel.Ultra:
|
|
dictionarySize = 0.858f;
|
|
break;
|
|
}
|
|
break;
|
|
case CompressionMethod.Deflate:
|
|
case CompressionMethod.Deflate64:
|
|
dictionarySize = 32;
|
|
break;
|
|
case CompressionMethod.Ppmd:
|
|
dictionarySize = 16;
|
|
break;
|
|
}
|
|
return dictionarySize;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Produces a new instance of ArchiveUpdateCallback class.
|
|
/// </summary>
|
|
/// <param name="files">Array of FileInfo - files to pack</param>
|
|
/// <param name="rootLength">Length of the common root of file names</param>
|
|
/// <param name="password">The archive password</param>
|
|
/// <returns></returns>
|
|
private ArchiveUpdateCallback GetArchiveUpdateCallback(
|
|
FileInfo[] files, int rootLength, string password)
|
|
{
|
|
SetCompressionProperties();
|
|
var auc = (String.IsNullOrEmpty(password))
|
|
? new ArchiveUpdateCallback(files, rootLength, this, GetUpdateData(), DirectoryStructure)
|
|
{ DictionarySize = GetDictionarySize() }
|
|
: new ArchiveUpdateCallback(files, rootLength, password, this, GetUpdateData(), DirectoryStructure)
|
|
{ DictionarySize = GetDictionarySize() };
|
|
CommonUpdateCallbackInit(auc);
|
|
return auc;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Produces a new instance of ArchiveUpdateCallback class.
|
|
/// </summary>
|
|
/// <param name="inStream">The archive input stream.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
/// <returns></returns>
|
|
private ArchiveUpdateCallback GetArchiveUpdateCallback(Stream inStream, string password)
|
|
{
|
|
SetCompressionProperties();
|
|
var auc = (String.IsNullOrEmpty(password))
|
|
? new ArchiveUpdateCallback(inStream, this, GetUpdateData(), DirectoryStructure)
|
|
{ DictionarySize = GetDictionarySize() }
|
|
: new ArchiveUpdateCallback(inStream, password, this, GetUpdateData(), DirectoryStructure)
|
|
{ DictionarySize = GetDictionarySize() };
|
|
CommonUpdateCallbackInit(auc);
|
|
return auc;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Produces a new instance of ArchiveUpdateCallback class.
|
|
/// </summary>
|
|
/// <param name="streamDict">Dictionary<name of the archive entry, stream>.</param>
|
|
/// <param name="password">The archive password</param>
|
|
/// <returns></returns>
|
|
private ArchiveUpdateCallback GetArchiveUpdateCallback(
|
|
Dictionary<string, Stream> streamDict, string password)
|
|
{
|
|
SetCompressionProperties();
|
|
var auc = (String.IsNullOrEmpty(password))
|
|
? new ArchiveUpdateCallback(streamDict, this, GetUpdateData(), DirectoryStructure)
|
|
{ DictionarySize = GetDictionarySize() }
|
|
: new ArchiveUpdateCallback(streamDict, password, this, GetUpdateData(), DirectoryStructure)
|
|
{ DictionarySize = GetDictionarySize() };
|
|
CommonUpdateCallbackInit(auc);
|
|
return auc;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Service "Get" functions
|
|
|
|
private void FreeCompressionCallback(ArchiveUpdateCallback callback)
|
|
{
|
|
callback.FileCompressionStarted -= FileCompressionStartedEventProxy;
|
|
callback.Compressing -= CompressingEventProxy;
|
|
callback.FileCompressionFinished -= FileCompressionFinishedEventProxy;
|
|
}
|
|
|
|
private string GetTempArchiveFileName(string archiveName)
|
|
{
|
|
return Path.Combine(TempFolderPath, Path.GetFileName(archiveName) + ".~");
|
|
}
|
|
|
|
private FileStream GetArchiveFileStream(string archiveName)
|
|
{
|
|
if ((CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null) && !File.Exists(archiveName))
|
|
{
|
|
if (
|
|
!ThrowException(null, new CompressionFailedException("file \"" + archiveName + "\" does not exist.")))
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
return _volumeSize == 0
|
|
? CompressionMode == CompressionMode.Create && _updateData.FileNamesToModify == null
|
|
? File.Create(archiveName)
|
|
: File.Create(GetTempArchiveFileName(archiveName))
|
|
: null;
|
|
}
|
|
|
|
private void FinalizeUpdate()
|
|
{
|
|
if (_volumeSize == 0 && (CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null))
|
|
{
|
|
File.Move(GetTempArchiveFileName(_archiveName), _archiveName);
|
|
}
|
|
}
|
|
|
|
private UpdateData GetUpdateData()
|
|
{
|
|
if (_updateData.FileNamesToModify == null)
|
|
{
|
|
var updateData = new UpdateData {Mode = (InternalCompressionMode) ((int) CompressionMode)};
|
|
switch (CompressionMode)
|
|
{
|
|
case CompressionMode.Create:
|
|
updateData.FilesCount = UInt32.MaxValue;
|
|
break;
|
|
case CompressionMode.Append:
|
|
updateData.FilesCount = _oldFilesCount;
|
|
break;
|
|
}
|
|
return updateData;
|
|
}
|
|
return _updateData;
|
|
}
|
|
|
|
private ISequentialOutStream GetOutStream(Stream outStream)
|
|
{
|
|
if (!_compressingFilesOnDisk)
|
|
{
|
|
return new OutStreamWrapper(outStream, false);
|
|
}
|
|
if (_volumeSize == 0 || CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null)
|
|
{
|
|
return new OutStreamWrapper(outStream, true);
|
|
}
|
|
return new OutMultiStreamWrapper(_archiveName, _volumeSize);
|
|
}
|
|
|
|
private IInStream GetInStream()
|
|
{
|
|
return File.Exists(_archiveName) &&
|
|
(CompressionMode != CompressionMode.Create && _compressingFilesOnDisk || _updateData.FileNamesToModify != null)
|
|
?
|
|
new InStreamWrapper(
|
|
new FileStream(_archiveName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
|
|
true)
|
|
: null;
|
|
}
|
|
|
|
private ArchiveOpenCallback GetArchiveOpenCallback()
|
|
{
|
|
return String.IsNullOrEmpty(Password)
|
|
?
|
|
new ArchiveOpenCallback(_archiveName)
|
|
:
|
|
new ArchiveOpenCallback(_archiveName, Password);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Core public Members
|
|
|
|
#region Events
|
|
|
|
/// <summary>
|
|
/// Occurs when the next file is going to be packed.
|
|
/// </summary>
|
|
/// <remarks>Occurs when 7-zip engine requests for an input stream for the next file to pack it</remarks>
|
|
public event EventHandler<FileNameEventArgs> FileCompressionStarted;
|
|
|
|
/// <summary>
|
|
/// Occurs when the current file was compressed.
|
|
/// </summary>
|
|
public event EventHandler<EventArgs> FileCompressionFinished;
|
|
|
|
/// <summary>
|
|
/// Occurs when data are being compressed
|
|
/// </summary>
|
|
/// <remarks>Use this event for accurate progress handling and various ProgressBar.StepBy(e.PercentDelta) routines</remarks>
|
|
public event EventHandler<ProgressEventArgs> Compressing;
|
|
|
|
/// <summary>
|
|
/// Occurs when all files information was determined and SevenZipCompressor is about to start to compress them.
|
|
/// </summary>
|
|
/// <remarks>The incoming int value indicates the number of scanned files.</remarks>
|
|
public event EventHandler<IntEventArgs> FilesFound;
|
|
|
|
/// <summary>
|
|
/// Occurs when the compression procedure is finished
|
|
/// </summary>
|
|
public event EventHandler<EventArgs> CompressionFinished;
|
|
|
|
#region Event proxies
|
|
/// <summary>
|
|
/// Event proxy for FileCompressionStarted.
|
|
/// </summary>
|
|
/// <param name="sender">The sender of the event.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void FileCompressionStartedEventProxy(object sender, FileNameEventArgs e)
|
|
{
|
|
OnEvent(FileCompressionStarted, e, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event proxy for FileCompressionFinished.
|
|
/// </summary>
|
|
/// <param name="sender">The sender of the event.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void FileCompressionFinishedEventProxy(object sender, EventArgs e)
|
|
{
|
|
OnEvent(FileCompressionFinished, e, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event proxy for Compressing.
|
|
/// </summary>
|
|
/// <param name="sender">The sender of the event.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void CompressingEventProxy(object sender, ProgressEventArgs e)
|
|
{
|
|
OnEvent(Compressing, e, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event proxy for FilesFound.
|
|
/// </summary>
|
|
/// <param name="sender">The sender of the event.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void FilesFoundEventProxy(object sender, IntEventArgs e)
|
|
{
|
|
OnEvent(FilesFound, e, false);
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Gets or sets the archive format
|
|
/// </summary>
|
|
public OutArchiveFormat ArchiveFormat
|
|
{
|
|
get
|
|
{
|
|
return _archiveFormat;
|
|
}
|
|
|
|
set
|
|
{
|
|
_archiveFormat = value;
|
|
if (!MethodIsValid(_compressionMethod))
|
|
{
|
|
_compressionMethod = CompressionMethod.Default;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the compression method
|
|
/// </summary>
|
|
public CompressionMethod CompressionMethod
|
|
{
|
|
get
|
|
{
|
|
return _compressionMethod;
|
|
}
|
|
|
|
set
|
|
{
|
|
_compressionMethod = !MethodIsValid(value) ? CompressionMethod.Default : value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the size in bytes of an archive volume (0 for no volumes).
|
|
/// </summary>
|
|
public int VolumeSize
|
|
{
|
|
get
|
|
{
|
|
return _volumeSize;
|
|
}
|
|
|
|
set
|
|
{
|
|
_volumeSize = value > 0 ? value : 0;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region CompressFiles overloads
|
|
|
|
/// <summary>
|
|
/// Packs files into the archive.
|
|
/// </summary>
|
|
/// <param name="fileFullNames">Array of file names to pack.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
public void CompressFiles(
|
|
string archiveName, params string[] fileFullNames)
|
|
{
|
|
CompressFilesEncrypted(archiveName, "", fileFullNames);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs files into the archive.
|
|
/// </summary>
|
|
/// <param name="fileFullNames">Array of file names to pack.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressFiles(string archiveName ... ) overloads for archiving to disk.</param>
|
|
public void CompressFiles(
|
|
Stream archiveStream, params string[] fileFullNames)
|
|
{
|
|
CompressFilesEncrypted(archiveStream, "", fileFullNames);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs files into the archive.
|
|
/// </summary>
|
|
/// <param name="fileFullNames">Array of file names to pack.</param>
|
|
/// <param name="commonRootLength">The length of the common root of the file names.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
public void CompressFiles(
|
|
string archiveName, int commonRootLength, params string[] fileFullNames)
|
|
{
|
|
CompressFilesEncrypted(archiveName, commonRootLength, "", fileFullNames);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs files into the archive.
|
|
/// </summary>
|
|
/// <param name="fileFullNames">Array of file names to pack.</param>
|
|
/// <param name="commonRootLength">The length of the common root of the file names.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressFiles(string archiveName, ... ) overloads for archiving to disk.</param>
|
|
public void CompressFiles(
|
|
Stream archiveStream, int commonRootLength, params string[] fileFullNames)
|
|
{
|
|
CompressFilesEncrypted(archiveStream, commonRootLength, "", fileFullNames);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs files into the archive.
|
|
/// </summary>
|
|
/// <param name="fileFullNames">Array of file names to pack.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressFilesEncrypted(
|
|
string archiveName, string password, params string[] fileFullNames)
|
|
{
|
|
CompressFilesEncrypted(archiveName, CommonRoot(fileFullNames), password, fileFullNames);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs files into the archive.
|
|
/// </summary>
|
|
/// <param name="fileFullNames">Array of file names to pack.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressFiles( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressFilesEncrypted(
|
|
Stream archiveStream, string password, params string[] fileFullNames)
|
|
{
|
|
CompressFilesEncrypted(archiveStream, CommonRoot(fileFullNames), password, fileFullNames);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs files into the archive.
|
|
/// </summary>
|
|
/// <param name="fileFullNames">Array of file names to pack.</param>
|
|
/// <param name="commonRootLength">The length of the common root of the file names.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressFilesEncrypted(
|
|
string archiveName, int commonRootLength, string password, params string[] fileFullNames)
|
|
{
|
|
_compressingFilesOnDisk = true;
|
|
_archiveName = archiveName;
|
|
using (FileStream fs = GetArchiveFileStream(archiveName))
|
|
{
|
|
if (fs == null)
|
|
{
|
|
return;
|
|
}
|
|
CompressFilesEncrypted(fs, commonRootLength, password, fileFullNames);
|
|
}
|
|
FinalizeUpdate();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs files into the archive.
|
|
/// </summary>
|
|
/// <param name="fileFullNames">Array of file names to pack.</param>
|
|
/// <param name="commonRootLength">The length of the common root of the file names.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressFiles( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressFilesEncrypted(
|
|
Stream archiveStream, int commonRootLength, string password, params string[] fileFullNames)
|
|
{
|
|
ClearExceptions();
|
|
if (fileFullNames.Length > 1 &&
|
|
(_archiveFormat == OutArchiveFormat.BZip2 || _archiveFormat == OutArchiveFormat.GZip ||
|
|
_archiveFormat == OutArchiveFormat.XZ))
|
|
{
|
|
if (!ThrowException(null,
|
|
new CompressionFailedException("Can not compress more than one file in this format.")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if (_volumeSize == 0 || !_compressingFilesOnDisk)
|
|
{
|
|
ValidateStream(archiveStream);
|
|
}
|
|
FileInfo[] files = null;
|
|
try
|
|
{
|
|
files = ProduceFileInfoArray(fileFullNames, commonRootLength, _directoryCompress, DirectoryStructure);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!ThrowException(null, e))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
_directoryCompress = false;
|
|
if (FilesFound != null)
|
|
{
|
|
FilesFound(this, new IntEventArgs(fileFullNames.Length));
|
|
}
|
|
try
|
|
{
|
|
ISequentialOutStream sequentialArchiveStream;
|
|
using ((sequentialArchiveStream = GetOutStream(archiveStream)) as IDisposable)
|
|
{
|
|
IInStream inArchiveStream;
|
|
using ((inArchiveStream = GetInStream()) as IDisposable)
|
|
{
|
|
IOutArchive outArchive;
|
|
if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
|
|
{
|
|
SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
|
|
outArchive = SevenZipLibraryManager.OutArchive(_archiveFormat, this);
|
|
}
|
|
else
|
|
{
|
|
// Create IInArchive, read it and convert to IOutArchive
|
|
SevenZipLibraryManager.LoadLibrary(
|
|
this, Formats.InForOutFormats[_archiveFormat]);
|
|
if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
using (var auc = GetArchiveUpdateCallback(files, commonRootLength, password))
|
|
{
|
|
try
|
|
{
|
|
if (files != null) //ReSharper
|
|
CheckedExecute(
|
|
outArchive.UpdateItems(
|
|
sequentialArchiveStream, (uint) files.Length + _oldFilesCount, auc),
|
|
SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
|
|
}
|
|
finally
|
|
{
|
|
FreeCompressionCallback(auc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
|
|
{
|
|
SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
|
|
}
|
|
else
|
|
{
|
|
SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
|
|
File.Delete(_archiveName);
|
|
}
|
|
_compressingFilesOnDisk = false;
|
|
OnEvent(CompressionFinished, EventArgs.Empty, false);
|
|
}
|
|
ThrowUserException();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CompressDirectory overloads
|
|
|
|
#if !CS4
|
|
/// <summary>
|
|
/// Recursively packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
public void CompressDirectory(
|
|
string directory, string archiveName)
|
|
{
|
|
CompressDirectory(directory, archiveName, "", "*", true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recursively packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
public void CompressDirectory(
|
|
string directory, Stream archiveStream)
|
|
{
|
|
CompressDirectory(directory, archiveStream, "", "*", true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recursively packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressDirectory(
|
|
string directory, string archiveName, string password)
|
|
{
|
|
CompressDirectory(directory, archiveName, password, "*", true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recursively packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressDirectory(
|
|
string directory, Stream archiveStream, string password)
|
|
{
|
|
CompressDirectory(directory, archiveStream, password, "*", true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
|
|
public void CompressDirectory(
|
|
string directory, string archiveName, bool recursion)
|
|
{
|
|
CompressDirectory(directory, archiveName, "", "*", recursion);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
|
|
public void CompressDirectory(
|
|
string directory, Stream archiveStream, bool recursion)
|
|
{
|
|
CompressDirectory(directory, archiveStream, "", "*", recursion);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs all files found by the specified pattern in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="searchPattern">Search string, such as "*.txt".</param>
|
|
/// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
|
|
public void CompressDirectory(
|
|
string directory, string archiveName,
|
|
string searchPattern, bool recursion)
|
|
{
|
|
CompressDirectory(directory, archiveName, "", searchPattern, recursion);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs all files found by the specified pattern in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="searchPattern">Search string, such as "*.txt".</param>
|
|
/// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
|
|
public void CompressDirectory(
|
|
string directory, Stream archiveStream,
|
|
string searchPattern, bool recursion)
|
|
{
|
|
CompressDirectory(directory, archiveStream, "", searchPattern, recursion);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressDirectory(
|
|
string directory, string archiveName,
|
|
bool recursion, string password)
|
|
{
|
|
CompressDirectory(directory, archiveName, password, "*", recursion);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressDirectory(
|
|
string directory, Stream archiveStream,
|
|
bool recursion, string password)
|
|
{
|
|
CompressDirectory(directory, archiveStream, password, "*", recursion);
|
|
}
|
|
#endif
|
|
|
|
#if !CS4
|
|
/// <summary>
|
|
/// Packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
/// <param name="searchPattern">Search string, such as "*.txt".</param>
|
|
/// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
|
|
public void CompressDirectory(
|
|
string directory, string archiveName,
|
|
string password, string searchPattern, bool recursion)
|
|
#else
|
|
/// <summary>
|
|
/// Packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
/// <param name="searchPattern">Search string, such as "*.txt".</param>
|
|
/// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
|
|
public void CompressDirectory(
|
|
string directory, string archiveName,
|
|
string password = "", string searchPattern = "*", bool recursion = true)
|
|
#endif
|
|
{
|
|
_compressingFilesOnDisk = true;
|
|
_archiveName = archiveName;
|
|
using (FileStream fs = GetArchiveFileStream(archiveName))
|
|
{
|
|
if (fs == null && _volumeSize == 0)
|
|
{
|
|
return;
|
|
}
|
|
CompressDirectory(directory, fs, password, searchPattern, recursion);
|
|
}
|
|
FinalizeUpdate();
|
|
}
|
|
|
|
#if !CS4
|
|
/// <summary>
|
|
/// Packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
/// <param name="searchPattern">Search string, such as "*.txt".</param>
|
|
/// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
|
|
public void CompressDirectory(
|
|
string directory, Stream archiveStream,
|
|
string password, string searchPattern, bool recursion)
|
|
#else
|
|
/// <summary>
|
|
/// Packs all files in the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory to compress.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
/// <param name="searchPattern">Search string, such as "*.txt".</param>
|
|
/// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
|
|
public void CompressDirectory(
|
|
string directory, Stream archiveStream,
|
|
string password = "", string searchPattern = "*", bool recursion = true)
|
|
#endif
|
|
{
|
|
var files = new List<string>();
|
|
if (!Directory.Exists(directory))
|
|
{
|
|
throw new ArgumentException("Directory \"" + directory + "\" does not exist!");
|
|
}
|
|
if (RecursiveDirectoryEmptyCheck(directory))
|
|
{
|
|
throw new SevenZipInvalidFileNamesException("the specified directory is empty!");
|
|
}
|
|
if (recursion)
|
|
{
|
|
AddFilesFromDirectory(directory, files, searchPattern);
|
|
}
|
|
else
|
|
{
|
|
#if CS4
|
|
files.AddRange((new DirectoryInfo(directory)).GetFiles(searchPattern).Select(fi => fi.FullName));
|
|
#else
|
|
foreach (FileInfo fi in (new DirectoryInfo(directory)).GetFiles(searchPattern))
|
|
{
|
|
files.Add(fi.FullName);
|
|
}
|
|
#endif
|
|
}
|
|
int commonRootLength = directory.Length;
|
|
if (directory.EndsWith("\\", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
directory = directory.Substring(0, directory.Length - 1);
|
|
}
|
|
else
|
|
{
|
|
commonRootLength++;
|
|
}
|
|
if (PreserveDirectoryRoot)
|
|
{
|
|
var upperRoot = Path.GetDirectoryName(directory);
|
|
commonRootLength = upperRoot.Length +
|
|
(upperRoot.EndsWith("\\", StringComparison.OrdinalIgnoreCase) ? 0 : 1);
|
|
}
|
|
_directoryCompress = true;
|
|
CompressFilesEncrypted(archiveStream, commonRootLength, password, files.ToArray());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CompressFileDictionary overloads
|
|
|
|
#if !CS4
|
|
/// <summary>
|
|
/// Packs the specified file dictionary.
|
|
/// </summary>
|
|
/// <param name="fileDictionary">Dictionary<name of the archive entry, file name>.
|
|
/// If a file name is null, the corresponding archive entry becomes a directory.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
public void CompressFileDictionary(
|
|
Dictionary<string, string> fileDictionary, string archiveName)
|
|
{
|
|
CompressFileDictionary(fileDictionary, archiveName, "");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs the specified file dictionary.
|
|
/// </summary>
|
|
/// <param name="fileDictionary">Dictionary<name of the archive entry, file name>.
|
|
/// If a file name is null, the corresponding archive entry becomes a directory.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressFileDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
public void CompressFileDictionary(
|
|
Dictionary<string, string> fileDictionary, Stream archiveStream)
|
|
{
|
|
CompressFileDictionary(fileDictionary, archiveStream, "");
|
|
}
|
|
#endif
|
|
|
|
#if !CS4
|
|
/// <summary>
|
|
/// Packs the specified file dictionary.
|
|
/// </summary>
|
|
/// <param name="fileDictionary">Dictionary<name of the archive entry, file name>.
|
|
/// If a file name is null, the corresponding archive entry becomes a directory.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressFileDictionary(
|
|
Dictionary<string, string> fileDictionary, string archiveName, string password)
|
|
#else
|
|
/// <summary>
|
|
/// Packs the specified file dictionary.
|
|
/// </summary>
|
|
/// <param name="fileDictionary">Dictionary<name of the archive entry, file name>.
|
|
/// If a file name is null, the corresponding archive entry becomes a directory.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressFileDictionary(
|
|
Dictionary<string, string> fileDictionary, string archiveName, string password = "")
|
|
#endif
|
|
{
|
|
_compressingFilesOnDisk = true;
|
|
_archiveName = archiveName;
|
|
using (FileStream fs = GetArchiveFileStream(archiveName))
|
|
{
|
|
if (fs == null)
|
|
{
|
|
return;
|
|
}
|
|
CompressFileDictionary(fileDictionary, fs, password);
|
|
}
|
|
FinalizeUpdate();
|
|
}
|
|
|
|
#if !CS4
|
|
/// <summary>
|
|
/// Packs the specified file dictionary.
|
|
/// </summary>
|
|
/// <param name="fileDictionary">Dictionary<name of the archive entry, file name>.
|
|
/// If a file name is null, the corresponding archive entry becomes a directory.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressFileDictionary(
|
|
Dictionary<string, string> fileDictionary, Stream archiveStream, string password)
|
|
#else
|
|
/// <summary>
|
|
/// Packs the specified file dictionary.
|
|
/// </summary>
|
|
/// <param name="fileDictionary">Dictionary<name of the archive entry, file name>.
|
|
/// If a file name is null, the corresponding archive entry becomes a directory.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressFileDictionary(
|
|
Dictionary<string, string> fileDictionary, Stream archiveStream, string password = "")
|
|
#endif
|
|
{
|
|
var streamDict = new Dictionary<string, Stream>(fileDictionary.Count);
|
|
foreach (var pair in fileDictionary)
|
|
{
|
|
if (pair.Value == null)
|
|
{
|
|
streamDict.Add(pair.Key, null);
|
|
}
|
|
else
|
|
{
|
|
if (!File.Exists(pair.Value))
|
|
{
|
|
throw new CompressionFailedException("The file corresponding to the archive entry \"" + pair.Key + "\" does not exist.");
|
|
}
|
|
streamDict.Add(
|
|
pair.Key,
|
|
new FileStream(pair.Value, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
|
|
}
|
|
}
|
|
//The created streams will be automatically disposed inside.
|
|
CompressStreamDictionary(streamDict, archiveStream, password);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CompressStreamDictionary overloads
|
|
#if !CS4
|
|
/// <summary>
|
|
/// Packs the specified stream dictionary.
|
|
/// </summary>
|
|
/// <param name="streamDictionary">Dictionary<name of the archive entry, stream>.
|
|
/// If a stream is null, the corresponding string becomes a directory name.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
public void CompressStreamDictionary(
|
|
Dictionary<string, Stream> streamDictionary, string archiveName)
|
|
{
|
|
CompressStreamDictionary(streamDictionary, archiveName, "");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Packs the specified stream dictionary.
|
|
/// </summary>
|
|
/// <param name="streamDictionary">Dictionary<name of the archive entry, stream>.
|
|
/// If a stream is null, the corresponding string becomes a directory name.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
public void CompressStreamDictionary(
|
|
Dictionary<string, Stream> streamDictionary, Stream archiveStream)
|
|
{
|
|
CompressStreamDictionary(streamDictionary, archiveStream, "");
|
|
}
|
|
#endif
|
|
|
|
#if !CS4
|
|
/// <summary>
|
|
/// Packs the specified stream dictionary.
|
|
/// </summary>
|
|
/// <param name="streamDictionary">Dictionary<name of the archive entry, stream>.
|
|
/// If a stream is null, the corresponding string becomes a directory name.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressStreamDictionary(
|
|
Dictionary<string, Stream> streamDictionary, string archiveName, string password)
|
|
#else
|
|
/// <summary>
|
|
/// Packs the specified stream dictionary.
|
|
/// </summary>
|
|
/// <param name="streamDictionary">Dictionary<name of the archive entry, stream>.
|
|
/// If a stream is null, the corresponding string becomes a directory name.</param>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressStreamDictionary(
|
|
Dictionary<string, Stream> streamDictionary, string archiveName, string password = "")
|
|
#endif
|
|
{
|
|
_compressingFilesOnDisk = true;
|
|
_archiveName = archiveName;
|
|
using (FileStream fs = GetArchiveFileStream(archiveName))
|
|
{
|
|
if (fs == null)
|
|
{
|
|
return;
|
|
}
|
|
CompressStreamDictionary(streamDictionary, fs, password);
|
|
}
|
|
FinalizeUpdate();
|
|
}
|
|
|
|
#if !CS4
|
|
/// <summary>
|
|
/// Packs the specified stream dictionary.
|
|
/// </summary>
|
|
/// <param name="streamDictionary">Dictionary<name of the archive entry, stream>.
|
|
/// If a stream is null, the corresponding string becomes a directory name.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressStreamDictionary(
|
|
Dictionary<string, Stream> streamDictionary, Stream archiveStream, string password)
|
|
#else
|
|
/// <summary>
|
|
/// Packs the specified stream dictionary.
|
|
/// </summary>
|
|
/// <param name="streamDictionary">Dictionary<name of the archive entry, stream>.
|
|
/// If a stream is null, the corresponding string becomes a directory name.</param>
|
|
/// <param name="archiveStream">The archive output stream.
|
|
/// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void CompressStreamDictionary(
|
|
Dictionary<string, Stream> streamDictionary, Stream archiveStream, string password = "")
|
|
#endif
|
|
{
|
|
ClearExceptions();
|
|
if (streamDictionary.Count > 1 &&
|
|
(_archiveFormat == OutArchiveFormat.BZip2 || _archiveFormat == OutArchiveFormat.GZip ||
|
|
_archiveFormat == OutArchiveFormat.XZ))
|
|
{
|
|
if (!ThrowException(null,
|
|
new CompressionFailedException("Can not compress more than one file/stream in this format.")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if (_volumeSize == 0 || !_compressingFilesOnDisk)
|
|
{
|
|
ValidateStream(archiveStream);
|
|
}
|
|
#if CS4
|
|
if (streamDictionary.Where(
|
|
pair => pair.Value != null && (!pair.Value.CanSeek || !pair.Value.CanRead)).Any(
|
|
pair => !ThrowException(null,
|
|
new ArgumentException("The specified stream dictionary contains an invalid stream corresponding to the archive entry \""
|
|
+ pair.Key + "\".", "streamDictionary")))) {
|
|
return;
|
|
}
|
|
#else
|
|
foreach (var pair in streamDictionary)
|
|
{
|
|
if (pair.Value != null && (!pair.Value.CanSeek || !pair.Value.CanRead))
|
|
{
|
|
if (!ThrowException(null, new ArgumentException(
|
|
"The specified stream dictionary contains an invalid stream corresponding to the archive entry \"" + pair.Key + "\".",
|
|
"streamDictionary")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
try
|
|
{
|
|
ISequentialOutStream sequentialArchiveStream;
|
|
using ((sequentialArchiveStream = GetOutStream(archiveStream)) as IDisposable)
|
|
{
|
|
IInStream inArchiveStream;
|
|
using ((inArchiveStream = GetInStream()) as IDisposable)
|
|
{
|
|
IOutArchive outArchive;
|
|
if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
|
|
{
|
|
SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
|
|
outArchive = SevenZipLibraryManager.OutArchive(_archiveFormat, this);
|
|
}
|
|
else
|
|
{
|
|
// Create IInArchive, read it and convert to IOutArchive
|
|
SevenZipLibraryManager.LoadLibrary(
|
|
this, Formats.InForOutFormats[_archiveFormat]);
|
|
if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
using (ArchiveUpdateCallback auc = GetArchiveUpdateCallback(
|
|
streamDictionary, password))
|
|
{
|
|
try
|
|
{
|
|
CheckedExecute(outArchive.UpdateItems(sequentialArchiveStream,
|
|
(uint)streamDictionary.Count + _oldFilesCount, auc),
|
|
SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
|
|
}
|
|
finally
|
|
{
|
|
FreeCompressionCallback(auc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
|
|
{
|
|
SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
|
|
}
|
|
else
|
|
{
|
|
SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
|
|
File.Delete(_archiveName);
|
|
}
|
|
_compressingFilesOnDisk = false;
|
|
OnEvent(CompressionFinished, EventArgs.Empty, false);
|
|
}
|
|
ThrowUserException();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CompressStream overloads
|
|
|
|
#if !CS4
|
|
/// <summary>
|
|
/// Compresses the specified stream.
|
|
/// </summary>
|
|
/// <param name="inStream">The source uncompressed stream.</param>
|
|
/// <param name="outStream">The destination compressed stream.</param>
|
|
/// <exception cref="ArgumentException">ArgumentException: at least one of the specified streams is invalid.</exception>
|
|
public void CompressStream(Stream inStream, Stream outStream)
|
|
{
|
|
CompressStream(inStream, outStream, "");
|
|
}
|
|
#endif
|
|
/// <summary>
|
|
/// Compresses the specified stream.
|
|
/// </summary>
|
|
/// <param name="inStream">The source uncompressed stream.</param>
|
|
/// <param name="outStream">The destination compressed stream.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
/// <exception cref="ArgumentException">ArgumentException: at least one of the specified streams is invalid.</exception>
|
|
public void CompressStream(Stream inStream, Stream outStream, string password
|
|
#if CS4
|
|
= ""
|
|
#endif
|
|
)
|
|
{
|
|
ClearExceptions();
|
|
if (!inStream.CanSeek || !inStream.CanRead || !outStream.CanWrite)
|
|
{
|
|
if (!ThrowException(null, new ArgumentException("The specified streams are invalid.")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
try
|
|
{
|
|
SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
|
|
ISequentialOutStream sequentialArchiveStream;
|
|
using ((sequentialArchiveStream = GetOutStream(outStream)) as IDisposable)
|
|
{
|
|
using (ArchiveUpdateCallback auc = GetArchiveUpdateCallback(inStream, password))
|
|
{
|
|
try
|
|
{
|
|
CheckedExecute(
|
|
SevenZipLibraryManager.OutArchive(_archiveFormat, this).UpdateItems(
|
|
sequentialArchiveStream, 1, auc),
|
|
SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
|
|
}
|
|
finally
|
|
{
|
|
FreeCompressionCallback(auc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
|
|
OnEvent(CompressionFinished, EventArgs.Empty, false);
|
|
}
|
|
ThrowUserException();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ModifyArchive overloads
|
|
#if !CS4
|
|
/// <summary>
|
|
/// Modifies the existing archive (renames files or deletes them).
|
|
/// </summary>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="newFileNames">New file names. Null value to delete the corresponding index.</param>
|
|
public void ModifyArchive(string archiveName, Dictionary<int, string> newFileNames)
|
|
{
|
|
ModifyArchive(archiveName, newFileNames, "");
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Modifies the existing archive (renames files or deletes them).
|
|
/// </summary>
|
|
/// <param name="archiveName">The archive file name.</param>
|
|
/// <param name="newFileNames">New file names. Null value to delete the corresponding index.</param>
|
|
/// <param name="password">The archive password.</param>
|
|
public void ModifyArchive(string archiveName, Dictionary<int, string> newFileNames, string password
|
|
#if CS4
|
|
= ""
|
|
#endif
|
|
)
|
|
{
|
|
ClearExceptions();
|
|
if (!SevenZipLibraryManager.ModifyCapable)
|
|
{
|
|
throw new SevenZipLibraryException("The specified 7zip native library does not support this method.");
|
|
}
|
|
if (!File.Exists(archiveName))
|
|
{
|
|
if (!ThrowException(null, new ArgumentException("The specified archive does not exist.", "archiveName")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if (newFileNames == null || newFileNames.Count == 0)
|
|
{
|
|
if (!ThrowException(null, new ArgumentException("Invalid new file names.", "newFileNames")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
try
|
|
{
|
|
using (var extr = new SevenZipExtractor(archiveName))
|
|
{
|
|
_updateData = new UpdateData();
|
|
var archiveData = new ArchiveFileInfo[extr.ArchiveFileData.Count];
|
|
extr.ArchiveFileData.CopyTo(archiveData, 0);
|
|
_updateData.ArchiveFileData = new List<ArchiveFileInfo>(archiveData);
|
|
}
|
|
_updateData.FileNamesToModify = newFileNames;
|
|
_updateData.Mode = InternalCompressionMode.Modify;
|
|
}
|
|
catch (SevenZipException e)
|
|
{
|
|
if (!ThrowException(null, e))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
try
|
|
{
|
|
ISequentialOutStream sequentialArchiveStream;
|
|
_compressingFilesOnDisk = true;
|
|
using ((sequentialArchiveStream = GetOutStream(GetArchiveFileStream(archiveName))) as IDisposable)
|
|
{
|
|
IInStream inArchiveStream;
|
|
_archiveName = archiveName;
|
|
using ((inArchiveStream = GetInStream()) as IDisposable)
|
|
{
|
|
IOutArchive outArchive;
|
|
// Create IInArchive, read it and convert to IOutArchive
|
|
SevenZipLibraryManager.LoadLibrary(
|
|
this, Formats.InForOutFormats[_archiveFormat]);
|
|
if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
|
|
{
|
|
return;
|
|
}
|
|
using (ArchiveUpdateCallback auc = GetArchiveUpdateCallback(null, 0, password))
|
|
{
|
|
UInt32 deleteCount = 0;
|
|
if (_updateData.FileNamesToModify != null)
|
|
{
|
|
#if CS4 // System.Linq of C# 4 is great
|
|
deleteCount = (UInt32)_updateData.FileNamesToModify.Sum(
|
|
pairDeleted => pairDeleted.Value == null ? 1 : 0);
|
|
#else
|
|
foreach(var pairDeleted in _updateData.FileNamesToModify)
|
|
{
|
|
if (pairDeleted.Value == null)
|
|
{
|
|
deleteCount++;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
try
|
|
{
|
|
CheckedExecute(
|
|
outArchive.UpdateItems(
|
|
sequentialArchiveStream, _oldFilesCount - deleteCount, auc),
|
|
SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
|
|
}
|
|
finally
|
|
{
|
|
FreeCompressionCallback(auc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
|
|
File.Delete(archiveName);
|
|
FinalizeUpdate();
|
|
_compressingFilesOnDisk = false;
|
|
_updateData.FileNamesToModify = null;
|
|
_updateData.ArchiveFileData = null;
|
|
OnEvent(CompressionFinished, EventArgs.Empty, false);
|
|
}
|
|
ThrowUserException();
|
|
}
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Gets or sets the dictionary size for the managed LZMA algorithm.
|
|
/// </summary>
|
|
public static int LzmaDictionarySize
|
|
{
|
|
get
|
|
{
|
|
return _lzmaDictionarySize;
|
|
}
|
|
set
|
|
{
|
|
_lzmaDictionarySize = value;
|
|
}
|
|
}
|
|
|
|
internal static void WriteLzmaProperties(Encoder encoder)
|
|
{
|
|
#region LZMA properties definition
|
|
|
|
CoderPropId[] propIDs =
|
|
{
|
|
CoderPropId.DictionarySize,
|
|
CoderPropId.PosStateBits,
|
|
CoderPropId.LitContextBits,
|
|
CoderPropId.LitPosBits,
|
|
CoderPropId.Algorithm,
|
|
CoderPropId.NumFastBytes,
|
|
CoderPropId.MatchFinder,
|
|
CoderPropId.EndMarker
|
|
};
|
|
object[] properties =
|
|
{
|
|
_lzmaDictionarySize,
|
|
2,
|
|
3,
|
|
0,
|
|
2,
|
|
256,
|
|
"bt4",
|
|
false
|
|
};
|
|
|
|
#endregion
|
|
|
|
encoder.SetCoderProperties(propIDs, properties);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compresses the specified stream with LZMA algorithm (C# inside)
|
|
/// </summary>
|
|
/// <param name="inStream">The source uncompressed stream</param>
|
|
/// <param name="outStream">The destination compressed stream</param>
|
|
/// <param name="inLength">The length of uncompressed data (null for inStream.Length)</param>
|
|
/// <param name="codeProgressEvent">The event for handling the code progress</param>
|
|
public static void CompressStream(Stream inStream, Stream outStream, int? inLength,
|
|
EventHandler<ProgressEventArgs> codeProgressEvent)
|
|
{
|
|
if (!inStream.CanRead || !outStream.CanWrite)
|
|
{
|
|
throw new ArgumentException("The specified streams are invalid.");
|
|
}
|
|
var encoder = new Encoder();
|
|
WriteLzmaProperties(encoder);
|
|
encoder.WriteCoderProperties(outStream);
|
|
long streamSize = inLength.HasValue ? inLength.Value : inStream.Length;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
outStream.WriteByte((byte) (streamSize >> (8*i)));
|
|
}
|
|
encoder.Code(inStream, outStream, -1, -1, new LzmaProgressCallback(streamSize, codeProgressEvent));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compresses byte array with LZMA algorithm (C# inside)
|
|
/// </summary>
|
|
/// <param name="data">Byte array to compress</param>
|
|
/// <returns>Compressed byte array</returns>
|
|
public static byte[] CompressBytes(byte[] data)
|
|
{
|
|
using (var inStream = new MemoryStream(data))
|
|
{
|
|
using (var outStream = new MemoryStream())
|
|
{
|
|
var encoder = new Encoder();
|
|
WriteLzmaProperties(encoder);
|
|
encoder.WriteCoderProperties(outStream);
|
|
long streamSize = inStream.Length;
|
|
for (int i = 0; i < 8; i++)
|
|
outStream.WriteByte((byte) (streamSize >> (8*i)));
|
|
encoder.Code(inStream, outStream, -1, -1, null);
|
|
return outStream.ToArray();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
} |