Upload 2.0.2
[physicsfs] / lzma / CPP / 7zip / Archive / 7z / 7zHandlerOut.cpp
1 // 7zHandlerOut.cpp
2
3 #include "StdAfx.h"
4
5 #include "7zHandler.h"
6 #include "7zOut.h"
7 #include "7zUpdate.h"
8
9 #include "../../../Windows/PropVariant.h"
10
11 #include "../../../Common/ComTry.h"
12 #include "../../../Common/StringToInt.h"
13 #include "../../IPassword.h"
14 #include "../../ICoder.h"
15
16 #include "../Common/ItemNameUtils.h"
17 #include "../Common/ParseProperties.h"
18
19 using namespace NWindows;
20
21 namespace NArchive {
22 namespace N7z {
23
24 static const wchar_t *kLZMAMethodName = L"LZMA";
25 static const wchar_t *kCopyMethod = L"Copy";
26 static const wchar_t *kDefaultMethodName = kLZMAMethodName;
27
28 static const UInt32 kLzmaAlgorithmX5 = 1;
29 static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2";
30 static const UInt32 kDictionaryForHeaders = 1 << 20;
31 static const UInt32 kNumFastBytesForHeaders = 273;
32 static const UInt32 kAlgorithmForHeaders = kLzmaAlgorithmX5;
33
34 static inline bool IsCopyMethod(const UString &methodName)
35   { return (methodName.CompareNoCase(kCopyMethod) == 0); }
36
37 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
38 {
39   *type = NFileTimeType::kWindows;
40   return S_OK;
41 }
42
43 HRESULT CHandler::SetPassword(CCompressionMethodMode &methodMode,
44     IArchiveUpdateCallback *updateCallback)
45 {
46   CMyComPtr<ICryptoGetTextPassword2> getTextPassword;
47   if (!getTextPassword)
48   {
49     CMyComPtr<IArchiveUpdateCallback> udateCallback2(updateCallback);
50     udateCallback2.QueryInterface(IID_ICryptoGetTextPassword2, &getTextPassword);
51   }
52   
53   if (getTextPassword)
54   {
55     CMyComBSTR password;
56     Int32 passwordIsDefined;
57     RINOK(getTextPassword->CryptoGetTextPassword2(
58         &passwordIsDefined, &password));
59     methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
60     if (methodMode.PasswordIsDefined)
61       methodMode.Password = password;
62   }
63   else
64     methodMode.PasswordIsDefined = false;
65   return S_OK;
66 }
67
68 HRESULT CHandler::SetCompressionMethod(
69     CCompressionMethodMode &methodMode,
70     CCompressionMethodMode &headerMethod)
71 {
72   HRESULT res = SetCompressionMethod(methodMode, _methods
73   #ifdef COMPRESS_MT
74   , _numThreads
75   #endif
76   );
77   RINOK(res);
78   methodMode.Binds = _binds;
79
80   if (_compressHeaders)
81   {
82     // headerMethod.Methods.Add(methodMode.Methods.Back());
83
84     CObjectVector<COneMethodInfo> headerMethodInfoVector;
85     COneMethodInfo oneMethodInfo;
86     oneMethodInfo.MethodName = kLZMAMethodName;
87     {
88       CProp property;
89       property.Id = NCoderPropID::kMatchFinder;
90       property.Value = kLzmaMatchFinderForHeaders;
91       oneMethodInfo.Properties.Add(property);
92     }
93     {
94       CProp property;
95       property.Id = NCoderPropID::kAlgorithm;
96       property.Value = kAlgorithmForHeaders;
97       oneMethodInfo.Properties.Add(property);
98     }
99     {
100       CProp property;
101       property.Id = NCoderPropID::kNumFastBytes;
102       property.Value = UInt32(kNumFastBytesForHeaders);
103       oneMethodInfo.Properties.Add(property);
104     }
105     {
106       CProp property;
107       property.Id = NCoderPropID::kDictionarySize;
108       property.Value = UInt32(kDictionaryForHeaders);
109       oneMethodInfo.Properties.Add(property);
110     }
111     headerMethodInfoVector.Add(oneMethodInfo);
112     HRESULT res = SetCompressionMethod(headerMethod, headerMethodInfoVector
113       #ifdef COMPRESS_MT
114       ,1
115       #endif
116     );
117     RINOK(res);
118   }
119   return S_OK;
120 }
121
122 HRESULT CHandler::SetCompressionMethod(
123     CCompressionMethodMode &methodMode,
124     CObjectVector<COneMethodInfo> &methodsInfo
125     #ifdef COMPRESS_MT
126     , UInt32 numThreads
127     #endif
128     )
129 {
130   UInt32 level = _level;
131   
132   if (methodsInfo.IsEmpty())
133   {
134     COneMethodInfo oneMethodInfo;
135     oneMethodInfo.MethodName = ((level == 0) ? kCopyMethod : kDefaultMethodName);
136     methodsInfo.Add(oneMethodInfo);
137   }
138
139   bool needSolid = false;
140   for(int i = 0; i < methodsInfo.Size(); i++)
141   {
142     COneMethodInfo &oneMethodInfo = methodsInfo[i];
143     SetCompressionMethod2(oneMethodInfo
144       #ifdef COMPRESS_MT
145       , numThreads
146       #endif
147       );
148
149     if (!IsCopyMethod(oneMethodInfo.MethodName))
150       needSolid = true;
151
152     CMethodFull methodFull;
153
154     if (!FindMethod(
155         EXTERNAL_CODECS_VARS
156         oneMethodInfo.MethodName, methodFull.Id, methodFull.NumInStreams, methodFull.NumOutStreams))
157       return E_INVALIDARG;
158     methodFull.Properties = oneMethodInfo.Properties;
159     methodMode.Methods.Add(methodFull);
160
161     if (!_numSolidBytesDefined)
162     {
163       for (int j = 0; j < methodFull.Properties.Size(); j++)
164       {
165         const CProp &prop = methodFull.Properties[j];
166         if ((prop.Id == NCoderPropID::kDictionarySize || 
167              prop.Id == NCoderPropID::kUsedMemorySize) && prop.Value.vt == VT_UI4)
168         {
169           _numSolidBytes = ((UInt64)prop.Value.ulVal) << 7;
170           const UInt64 kMinSize = (1 << 24);
171           if (_numSolidBytes < kMinSize)
172             _numSolidBytes = kMinSize;
173           _numSolidBytesDefined = true;
174           break;
175         }
176       }
177     }
178   }
179
180   if (!needSolid && !_numSolidBytesDefined)
181   {
182     _numSolidBytesDefined = true;
183     _numSolidBytes  = 0;
184   }
185   return S_OK;
186 }
187
188 static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, PROPID propID, CArchiveFileTime &filetime, bool &filetimeIsDefined)
189 {
190   filetimeIsDefined = false;
191   NCOM::CPropVariant propVariant;
192   RINOK(updateCallback->GetProperty(index, propID, &propVariant));
193   if (propVariant.vt == VT_FILETIME)
194   {
195     filetime = propVariant.filetime;
196     filetimeIsDefined = true;
197   }
198   else if (propVariant.vt != VT_EMPTY)
199     return E_INVALIDARG;
200   return S_OK;
201 }
202
203 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
204     IArchiveUpdateCallback *updateCallback)
205 {
206   COM_TRY_BEGIN
207
208   const CArchiveDatabaseEx *database = 0;
209   #ifdef _7Z_VOL
210   if(_volumes.Size() > 1)
211     return E_FAIL;
212   const CVolume *volume = 0;
213   if (_volumes.Size() == 1)
214   {
215     volume = &_volumes.Front();
216     database = &volume->Database;
217   }
218   #else
219   if (_inStream != 0)
220     database = &_database;
221   #endif
222
223   // CRecordVector<bool> compressStatuses;
224   CObjectVector<CUpdateItem> updateItems;
225   // CRecordVector<UInt32> copyIndices;
226   
227   // CMyComPtr<IUpdateCallback2> updateCallback2;
228   // updateCallback->QueryInterface(&updateCallback2);
229
230   for(UInt32 i = 0; i < numItems; i++)
231   {
232     Int32 newData;
233     Int32 newProperties;
234     UInt32 indexInArchive;
235     if (!updateCallback)
236       return E_FAIL;
237     RINOK(updateCallback->GetUpdateItemInfo(i,
238         &newData, &newProperties, &indexInArchive));
239     CUpdateItem updateItem;
240     updateItem.NewProperties = IntToBool(newProperties);
241     updateItem.NewData = IntToBool(newData);
242     updateItem.IndexInArchive = indexInArchive;
243     updateItem.IndexInClient = i;
244     updateItem.IsAnti = false;
245     updateItem.Size = 0;
246
247     if (updateItem.IndexInArchive != -1)
248     {
249       const CFileItem &fileItem = database->Files[updateItem.IndexInArchive];
250       updateItem.Name = fileItem.Name;
251       updateItem.IsDirectory = fileItem.IsDirectory;
252       updateItem.Size = fileItem.UnPackSize;
253       updateItem.IsAnti = fileItem.IsAnti;
254       
255       updateItem.CreationTime = fileItem.CreationTime;
256       updateItem.IsCreationTimeDefined = fileItem.IsCreationTimeDefined;
257       updateItem.LastWriteTime = fileItem.LastWriteTime;
258       updateItem.IsLastWriteTimeDefined = fileItem.IsLastWriteTimeDefined;
259       updateItem.LastAccessTime = fileItem.LastAccessTime;
260       updateItem.IsLastAccessTimeDefined = fileItem.IsLastAccessTimeDefined;
261     }
262
263     if (updateItem.NewProperties)
264     {
265       bool nameIsDefined;
266       bool folderStatusIsDefined;
267       {
268         NCOM::CPropVariant propVariant;
269         RINOK(updateCallback->GetProperty(i, kpidAttributes, &propVariant));
270         if (propVariant.vt == VT_EMPTY)
271           updateItem.AttributesAreDefined = false;
272         else if (propVariant.vt != VT_UI4)
273           return E_INVALIDARG;
274         else
275         {
276           updateItem.Attributes = propVariant.ulVal;
277           updateItem.AttributesAreDefined = true;
278         }
279       }
280       
281       RINOK(GetTime(updateCallback, i, kpidCreationTime, updateItem.CreationTime, updateItem.IsCreationTimeDefined));
282       RINOK(GetTime(updateCallback, i, kpidLastWriteTime, updateItem.LastWriteTime , updateItem.IsLastWriteTimeDefined));
283       RINOK(GetTime(updateCallback, i, kpidLastAccessTime, updateItem.LastAccessTime, updateItem.IsLastAccessTimeDefined));
284
285       {
286         NCOM::CPropVariant propVariant;
287         RINOK(updateCallback->GetProperty(i, kpidPath, &propVariant));
288         if (propVariant.vt == VT_EMPTY)
289           nameIsDefined = false;
290         else if (propVariant.vt != VT_BSTR)
291           return E_INVALIDARG;
292         else
293         {
294           updateItem.Name = NItemName::MakeLegalName(propVariant.bstrVal);
295           nameIsDefined = true;
296         }
297       }
298       {
299         NCOM::CPropVariant propVariant;
300         RINOK(updateCallback->GetProperty(i, kpidIsFolder, &propVariant));
301         if (propVariant.vt == VT_EMPTY)
302           folderStatusIsDefined = false;
303         else if (propVariant.vt != VT_BOOL)
304           return E_INVALIDARG;
305         else
306         {
307           updateItem.IsDirectory = (propVariant.boolVal != VARIANT_FALSE);
308           folderStatusIsDefined = true;
309         }
310       }
311
312       {
313         NCOM::CPropVariant propVariant;
314         RINOK(updateCallback->GetProperty(i, kpidIsAnti, &propVariant));
315         if (propVariant.vt == VT_EMPTY)
316           updateItem.IsAnti = false;
317         else if (propVariant.vt != VT_BOOL)
318           return E_INVALIDARG;
319         else
320           updateItem.IsAnti = (propVariant.boolVal != VARIANT_FALSE);
321       }
322
323       if (updateItem.IsAnti)
324       {
325         updateItem.AttributesAreDefined = false;
326
327         updateItem.IsCreationTimeDefined = false;
328         updateItem.IsLastWriteTimeDefined = false;
329         updateItem.IsLastAccessTimeDefined = false;
330         
331         updateItem.Size = 0;
332       }
333
334       if (!folderStatusIsDefined && updateItem.AttributesAreDefined)
335         updateItem.SetDirectoryStatusFromAttributes();
336     }
337
338     if (updateItem.NewData)
339     {
340       NCOM::CPropVariant propVariant;
341       RINOK(updateCallback->GetProperty(i, kpidSize, &propVariant));
342       if (propVariant.vt != VT_UI8)
343         return E_INVALIDARG;
344       updateItem.Size = (UInt64)propVariant.uhVal.QuadPart;
345       if (updateItem.Size != 0 && updateItem.IsAnti)
346         return E_INVALIDARG;
347     }
348     updateItems.Add(updateItem);
349   }
350
351   CCompressionMethodMode methodMode, headerMethod;
352   RINOK(SetCompressionMethod(methodMode, headerMethod));
353   #ifdef COMPRESS_MT
354   methodMode.NumThreads = _numThreads;
355   headerMethod.NumThreads = 1;
356   #endif
357
358   RINOK(SetPassword(methodMode, updateCallback));
359
360   bool compressMainHeader = _compressHeaders;  // check it
361
362   if (methodMode.PasswordIsDefined)
363   {
364     compressMainHeader = true; 
365     if(_encryptHeaders)
366       RINOK(SetPassword(headerMethod, updateCallback));
367   }
368
369   if (numItems < 2)
370     compressMainHeader = false;
371
372   CUpdateOptions options;
373   options.Method = &methodMode;
374   options.HeaderMethod = (_compressHeaders || 
375       (methodMode.PasswordIsDefined && _encryptHeaders)) ? 
376       &headerMethod : 0;
377   options.UseFilters = _level != 0 && _autoFilter;
378   options.MaxFilter = _level >= 8;
379
380   options.HeaderOptions.CompressMainHeader = compressMainHeader;
381   options.HeaderOptions.WriteModified = WriteModified;
382   options.HeaderOptions.WriteCreated = WriteCreated;
383   options.HeaderOptions.WriteAccessed = WriteAccessed;
384   
385   options.NumSolidFiles = _numSolidFiles;
386   options.NumSolidBytes = _numSolidBytes;
387   options.SolidExtension = _solidExtension;
388   options.RemoveSfxBlock = _removeSfxBlock;
389   options.VolumeMode = _volumeMode;
390   return Update(
391       EXTERNAL_CODECS_VARS
392       #ifdef _7Z_VOL
393       volume ? volume->Stream: 0, 
394       volume ? database: 0, 
395       #else
396       _inStream, 
397       database,
398       #endif
399       updateItems, outStream, updateCallback, options);
400   COM_TRY_END
401 }
402
403 static HRESULT GetBindInfoPart(UString &srcString, UInt32 &coder, UInt32 &stream)
404 {
405   stream = 0;
406   int index = ParseStringToUInt32(srcString, coder);
407   if (index == 0)
408     return E_INVALIDARG;
409   srcString.Delete(0, index);
410   if (srcString[0] == 'S')
411   {
412     srcString.Delete(0);
413     int index = ParseStringToUInt32(srcString, stream);
414     if (index == 0)
415       return E_INVALIDARG;
416     srcString.Delete(0, index);
417   }
418   return S_OK;
419 }
420
421 static HRESULT GetBindInfo(UString &srcString, CBind &bind)
422 {
423   RINOK(GetBindInfoPart(srcString, bind.OutCoder, bind.OutStream));
424   if (srcString[0] != ':')
425     return E_INVALIDARG;
426   srcString.Delete(0);
427   RINOK(GetBindInfoPart(srcString, bind.InCoder, bind.InStream));
428   if (!srcString.IsEmpty())
429     return E_INVALIDARG;
430   return S_OK;
431 }
432
433 STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties)
434 {
435   COM_TRY_BEGIN
436   _binds.Clear();
437   BeforeSetProperty();
438
439   for (int i = 0; i < numProperties; i++)
440   {
441     UString name = names[i];
442     name.MakeUpper();
443     if (name.IsEmpty())
444       return E_INVALIDARG;
445
446     const PROPVARIANT &value = values[i];
447
448     if (name[0] == 'B')
449     {
450       name.Delete(0);
451       CBind bind;
452       RINOK(GetBindInfo(name, bind));
453       _binds.Add(bind);
454       continue;
455     }
456
457     RINOK(SetProperty(name, value));
458   }
459
460   return S_OK;
461   COM_TRY_END
462 }  
463
464 }}