2013-10-25 00:59:34 +00:00
/ * 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>
2014-07-22 07:47:21 +00:00
/// Class to pack data into archives supported by 7-Zip.
2013-10-25 00:59:34 +00:00
/// </summary>
2014-07-22 07:47:21 +00:00
/// <example>
/// var compr = new SevenZipCompressor();
/// compr.CompressDirectory(@"C:\Dir", @"C:\Archive.7z");
/// </example>
2013-10-25 00:59:34 +00:00
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
2014-07-22 07:47:21 +00:00
private static volatile int _lzmaDictionarySize = 1 < < 22 ;
2013-10-25 00:59:34 +00:00
#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 ( )
2014-07-22 07:47:21 +00:00
{
2013-10-25 00:59:34 +00:00
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." ) ;
}
2014-07-22 07:47:21 +00:00
CommonInit ( ) ;
2013-10-25 00:59:34 +00:00
}
/// <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 ;
2014-07-22 07:47:21 +00:00
if ( inArchive . Open ( inArchiveStream , ref checkPos , openCallback ) ! = ( int ) OperationResult . Ok )
2013-10-25 00:59:34 +00:00
{
if (
! ThrowException ( null , new SevenZipArchiveException ( "Can not update the archive: Open() failed." ) ) )
{
return null ;
}
}
_oldFilesCount = inArchive . GetNumberOfItems ( ) ;
}
2014-07-22 07:47:21 +00:00
return ( IOutArchive ) inArchive ;
2013-10-25 00:59:34 +00:00
}
/// <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
2014-07-22 07:47:21 +00:00
? ( ISetProperties ) SevenZipLibraryManager . OutArchive (
2013-10-25 00:59:34 +00:00
_archiveFormat , this )
2014-07-22 07:47:21 +00:00
: ( ISetProperties ) SevenZipLibraryManager . InArchive (
2013-10-25 00:59:34 +00:00
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
2014-07-22 07:47:21 +00:00
{
VarType = VarEnum . VT_BSTR ,
Value = Marshal . StringToBSTR ( Formats . MethodNames [ _compressionMethod ] )
} ;
2013-10-25 00:59:34 +00:00
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" ) ) ;
2014-07-22 07:47:21 +00:00
var tmp = new PropVariant { VarType = VarEnum . VT_BSTR , Value = Marshal . StringToBSTR ( "on" ) } ;
2013-10-25 00:59:34 +00:00
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
2014-07-22 07:47:21 +00:00
Enum . GetName ( typeof ( ZipEncryptionMethod ) , ZipEncryptionMethod ) )
2013-10-25 00:59:34 +00:00
#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 ;
2014-07-22 07:47:21 +00:00
foreach ( string t in splittedAfn )
{
2013-10-25 00:59:34 +00:00
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
{
2014-07-22 07:47:21 +00:00
using ( fi . OpenWrite ( ) ) { }
2013-10-25 00:59:34 +00:00
files . Add ( fi . FullName ) ;
}
2014-07-22 07:47:21 +00:00
catch ( IOException ) { }
2013-10-25 00:59:34 +00:00
}
}
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 ;
2014-07-22 07:47:21 +00:00
auc . FastCompression = FastCompression ;
2013-10-25 00:59:34 +00:00
}
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 )
{
2014-07-22 07:47:21 +00:00
var updateData = new UpdateData { Mode = ( InternalCompressionMode ) ( ( int ) CompressionMode ) } ;
2013-10-25 00:59:34 +00:00
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 ;
}
2014-07-22 07:47:21 +00:00
}
2013-10-25 00:59:34 +00:00
/// <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 ) )
{
2014-07-22 07:47:21 +00:00
if ( fs = = null & & _volumeSize = = 0 )
2013-10-25 00:59:34 +00:00
{
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 (
2014-07-22 07:47:21 +00:00
sequentialArchiveStream , ( uint ) files . Length + _oldFilesCount , auc ) ,
2013-10-25 00:59:34 +00:00
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 ( ) ;
2014-07-22 07:47:21 +00:00
}
2013-10-25 00:59:34 +00:00
#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
2014-07-22 07:47:21 +00:00
2013-10-25 00:59:34 +00:00
#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 ) )
{
2014-07-22 07:47:21 +00:00
if ( fs = = null & & _volumeSize = = 0 )
2013-10-25 00:59:34 +00:00
{
return ;
}
CompressFileDictionary ( fileDictionary , fs , password ) ;
}
FinalizeUpdate ( ) ;
2014-07-22 07:47:21 +00:00
}
2013-10-25 00:59:34 +00:00
#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
2014-07-22 07:47:21 +00:00
2013-10-25 00:59:34 +00:00
#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 ) )
{
2014-07-22 07:47:21 +00:00
if ( fs = = null & & _volumeSize = = 0 )
2013-10-25 00:59:34 +00:00
{
return ;
}
CompressStreamDictionary ( streamDictionary , fs , password ) ;
}
FinalizeUpdate ( ) ;
2014-07-22 07:47:21 +00:00
}
2013-10-25 00:59:34 +00:00
#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 \""
2014-07-22 07:47:21 +00:00
+ pair . Key + "\"." , "streamDictionary" ) ) ) )
{
2013-10-25 00:59:34 +00:00
return ;
}
#else
foreach ( var pair in streamDictionary )
{
2014-10-11 21:26:39 +00:00
if ( pair . Value ! = null & & (
//!pair.Value.CanSeek || //zero 11-oct-2014 - this is dumb, and I made it no longer necessary
! pair . Value . CanRead ) )
2013-10-25 00:59:34 +00:00
{
if ( ! ThrowException ( null , new ArgumentException (
"The specified stream dictionary contains an invalid stream corresponding to the archive entry \"" + pair . Key + "\"." ,
"streamDictionary" ) ) )
{
return ;
}
}
}
2014-07-22 07:47:21 +00:00
#endif
2013-10-25 00:59:34 +00:00
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
2014-07-22 07:47:21 +00:00
2013-10-25 00:59:34 +00:00
/// <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 ) ;
}
2014-07-22 07:47:21 +00:00
}
2013-10-25 00:59:34 +00:00
}
}
}
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 ( ) ;
2014-07-22 07:47:21 +00:00
}
2013-10-25 00:59:34 +00:00
#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 + + )
{
2014-07-22 07:47:21 +00:00
outStream . WriteByte ( ( byte ) ( streamSize > > ( 8 * i ) ) ) ;
2013-10-25 00:59:34 +00:00
}
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 + + )
2014-07-22 07:47:21 +00:00
outStream . WriteByte ( ( byte ) ( streamSize > > ( 8 * i ) ) ) ;
2013-10-25 00:59:34 +00:00
encoder . Code ( inStream , outStream , - 1 , - 1 , null ) ;
return outStream . ToArray ( ) ;
}
}
}
}
#endif
}