1// 7zHandler.cpp
2
3#include "StdAfx.h"
4
5#include "../../../../C/CpuArch.h"
6
7#include "../../../Common/ComTry.h"
8#include "../../../Common/IntToString.h"
9
10#ifndef __7Z_SET_PROPERTIES
11#include "../../../Windows/System.h"
12#endif
13
14#include "../Common/ItemNameUtils.h"
15
16#include "7zHandler.h"
17#include "7zProperties.h"
18
19#ifdef __7Z_SET_PROPERTIES
20#ifdef EXTRACT_ONLY
21#include "../Common/ParseProperties.h"
22#endif
23#endif
24
25using namespace NWindows;
26
27extern UString ConvertMethodIdToString(UInt64 id);
28
29namespace NArchive {
30namespace N7z {
31
32CHandler::CHandler()
33{
34  _crcSize = 4;
35
36  #ifndef _NO_CRYPTO
37  _passwordIsDefined = false;
38  #endif
39
40  #ifdef EXTRACT_ONLY
41  #ifdef __7Z_SET_PROPERTIES
42  _numThreads = NSystem::GetNumberOfProcessors();
43  #endif
44  #else
45  Init();
46  #endif
47}
48
49STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
50{
51  *numItems = _db.Files.Size();
52  return S_OK;
53}
54
55#ifdef _SFX
56
57IMP_IInArchive_ArcProps_NO
58
59STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 * /* numProperties */)
60{
61  return E_NOTIMPL;
62}
63
64STDMETHODIMP CHandler::GetPropertyInfo(UInt32 /* index */,
65      BSTR * /* name */, PROPID * /* propID */, VARTYPE * /* varType */)
66{
67  return E_NOTIMPL;
68}
69
70
71#else
72
73STATPROPSTG kArcProps[] =
74{
75  { NULL, kpidMethod, VT_BSTR},
76  { NULL, kpidSolid, VT_BOOL},
77  { NULL, kpidNumBlocks, VT_UI4},
78  { NULL, kpidPhySize, VT_UI8},
79  { NULL, kpidHeadersSize, VT_UI8},
80  { NULL, kpidOffset, VT_UI8}
81};
82
83STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
84{
85  COM_TRY_BEGIN
86  NCOM::CPropVariant prop;
87  switch(propID)
88  {
89    case kpidMethod:
90    {
91      UString resString;
92      CRecordVector<UInt64> ids;
93      int i;
94      for (i = 0; i < _db.Folders.Size(); i++)
95      {
96        const CFolder &f = _db.Folders[i];
97        for (int j = f.Coders.Size() - 1; j >= 0; j--)
98          ids.AddToUniqueSorted(f.Coders[j].MethodID);
99      }
100
101      for (i = 0; i < ids.Size(); i++)
102      {
103        UInt64 id = ids[i];
104        UString methodName;
105        /* bool methodIsKnown = */ FindMethod(EXTERNAL_CODECS_VARS id, methodName);
106        if (methodName.IsEmpty())
107          methodName = ConvertMethodIdToString(id);
108        if (!resString.IsEmpty())
109          resString += L' ';
110        resString += methodName;
111      }
112      prop = resString;
113      break;
114    }
115    case kpidSolid: prop = _db.IsSolid(); break;
116    case kpidNumBlocks: prop = (UInt32)_db.Folders.Size(); break;
117    case kpidHeadersSize:  prop = _db.HeadersSize; break;
118    case kpidPhySize:  prop = _db.PhySize; break;
119    case kpidOffset: if (_db.ArchiveInfo.StartPosition != 0) prop = _db.ArchiveInfo.StartPosition; break;
120  }
121  prop.Detach(value);
122  return S_OK;
123  COM_TRY_END
124}
125
126IMP_IInArchive_ArcProps
127
128#endif
129
130static void SetPropFromUInt64Def(CUInt64DefVector &v, int index, NCOM::CPropVariant &prop)
131{
132  UInt64 value;
133  if (v.GetItem(index, value))
134  {
135    FILETIME ft;
136    ft.dwLowDateTime = (DWORD)value;
137    ft.dwHighDateTime = (DWORD)(value >> 32);
138    prop = ft;
139  }
140}
141
142#ifndef _SFX
143
144static UString ConvertUInt32ToString(UInt32 value)
145{
146  wchar_t buffer[32];
147  ConvertUInt64ToString(value, buffer);
148  return buffer;
149}
150
151static UString GetStringForSizeValue(UInt32 value)
152{
153  for (int i = 31; i >= 0; i--)
154    if ((UInt32(1) << i) == value)
155      return ConvertUInt32ToString(i);
156  UString result;
157  if (value % (1 << 20) == 0)
158  {
159    result += ConvertUInt32ToString(value >> 20);
160    result += L"m";
161  }
162  else if (value % (1 << 10) == 0)
163  {
164    result += ConvertUInt32ToString(value >> 10);
165    result += L"k";
166  }
167  else
168  {
169    result += ConvertUInt32ToString(value);
170    result += L"b";
171  }
172  return result;
173}
174
175static const UInt64 k_Copy = 0x0;
176static const UInt64 k_Delta = 3;
177static const UInt64 k_LZMA2 = 0x21;
178static const UInt64 k_LZMA  = 0x030101;
179static const UInt64 k_PPMD  = 0x030401;
180
181static wchar_t GetHex(Byte value)
182{
183  return (wchar_t)((value < 10) ? (L'0' + value) : (L'A' + (value - 10)));
184}
185static inline void AddHexToString(UString &res, Byte value)
186{
187  res += GetHex((Byte)(value >> 4));
188  res += GetHex((Byte)(value & 0xF));
189}
190
191#endif
192
193bool CHandler::IsEncrypted(UInt32 index2) const
194{
195  CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
196  if (folderIndex != kNumNoIndex)
197    return _db.Folders[folderIndex].IsEncrypted();
198  return false;
199}
200
201STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID,  PROPVARIANT *value)
202{
203  COM_TRY_BEGIN
204  NCOM::CPropVariant prop;
205
206  /*
207  const CRef2 &ref2 = _refs[index];
208  if (ref2.Refs.IsEmpty())
209    return E_FAIL;
210  const CRef &ref = ref2.Refs.Front();
211  */
212
213  const CFileItem &item = _db.Files[index];
214  UInt32 index2 = index;
215
216  switch(propID)
217  {
218    case kpidPath:
219      if (!item.Name.IsEmpty())
220        prop = NItemName::GetOSName(item.Name);
221      break;
222    case kpidIsDir:  prop = item.IsDir; break;
223    case kpidSize:
224    {
225      prop = item.Size;
226      // prop = ref2.Size;
227      break;
228    }
229    case kpidPackSize:
230    {
231      // prop = ref2.PackSize;
232      {
233        CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
234        if (folderIndex != kNumNoIndex)
235        {
236          if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2)
237            prop = _db.GetFolderFullPackSize(folderIndex);
238          /*
239          else
240            prop = (UInt64)0;
241          */
242        }
243        else
244          prop = (UInt64)0;
245      }
246      break;
247    }
248    case kpidPosition:  { UInt64 v; if (_db.StartPos.GetItem(index2, v)) prop = v; break; }
249    case kpidCTime:  SetPropFromUInt64Def(_db.CTime, index2, prop); break;
250    case kpidATime:  SetPropFromUInt64Def(_db.ATime, index2, prop); break;
251    case kpidMTime:  SetPropFromUInt64Def(_db.MTime, index2, prop); break;
252    case kpidAttrib:  if (item.AttribDefined) prop = item.Attrib; break;
253    case kpidCRC:  if (item.CrcDefined) prop = item.Crc; break;
254    case kpidEncrypted:  prop = IsEncrypted(index2); break;
255    case kpidIsAnti:  prop = _db.IsItemAnti(index2); break;
256    #ifndef _SFX
257    case kpidMethod:
258      {
259        CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
260        if (folderIndex != kNumNoIndex)
261        {
262          const CFolder &folderInfo = _db.Folders[folderIndex];
263          UString methodsString;
264          for (int i = folderInfo.Coders.Size() - 1; i >= 0; i--)
265          {
266            const CCoderInfo &coder = folderInfo.Coders[i];
267            if (!methodsString.IsEmpty())
268              methodsString += L' ';
269
270            UString methodName, propsString;
271            bool methodIsKnown = FindMethod(
272              EXTERNAL_CODECS_VARS
273              coder.MethodID, methodName);
274
275            if (!methodIsKnown)
276              methodsString += ConvertMethodIdToString(coder.MethodID);
277            else
278            {
279              methodsString += methodName;
280              if (coder.MethodID == k_Delta && coder.Props.GetCapacity() == 1)
281                propsString = ConvertUInt32ToString((UInt32)coder.Props[0] + 1);
282              else if (coder.MethodID == k_LZMA && coder.Props.GetCapacity() == 5)
283              {
284                UInt32 dicSize = GetUi32((const Byte *)coder.Props + 1);
285                propsString = GetStringForSizeValue(dicSize);
286              }
287              else if (coder.MethodID == k_LZMA2 && coder.Props.GetCapacity() == 1)
288              {
289                Byte p = coder.Props[0];
290                UInt32 dicSize = (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11));
291                propsString = GetStringForSizeValue(dicSize);
292              }
293              else if (coder.MethodID == k_PPMD && coder.Props.GetCapacity() == 5)
294              {
295                Byte order = *(const Byte *)coder.Props;
296                propsString = L'o';
297                propsString += ConvertUInt32ToString(order);
298                propsString += L":mem";
299                UInt32 dicSize = GetUi32((const Byte *)coder.Props + 1);
300                propsString += GetStringForSizeValue(dicSize);
301              }
302              else if (coder.MethodID == k_AES && coder.Props.GetCapacity() >= 1)
303              {
304                const Byte *data = (const Byte *)coder.Props;
305                Byte firstByte = *data++;
306                UInt32 numCyclesPower = firstByte & 0x3F;
307                propsString = ConvertUInt32ToString(numCyclesPower);
308                /*
309                if ((firstByte & 0xC0) != 0)
310                {
311                  UInt32 saltSize = (firstByte >> 7) & 1;
312                  UInt32 ivSize = (firstByte >> 6) & 1;
313                  if (coder.Props.GetCapacity() >= 2)
314                  {
315                    Byte secondByte = *data++;
316                    saltSize += (secondByte >> 4);
317                    ivSize += (secondByte & 0x0F);
318                  }
319                }
320                */
321              }
322            }
323            if (!propsString.IsEmpty())
324            {
325              methodsString += L':';
326              methodsString += propsString;
327            }
328            else if (coder.Props.GetCapacity() > 0)
329            {
330              methodsString += L":[";
331              for (size_t bi = 0; bi < coder.Props.GetCapacity(); bi++)
332              {
333                if (bi > 5 && bi + 1 < coder.Props.GetCapacity())
334                {
335                  methodsString += L"..";
336                  break;
337                }
338                else
339                  AddHexToString(methodsString, coder.Props[bi]);
340              }
341              methodsString += L']';
342            }
343          }
344          prop = methodsString;
345        }
346      }
347      break;
348    case kpidBlock:
349      {
350        CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
351        if (folderIndex != kNumNoIndex)
352          prop = (UInt32)folderIndex;
353      }
354      break;
355    case kpidPackedSize0:
356    case kpidPackedSize1:
357    case kpidPackedSize2:
358    case kpidPackedSize3:
359    case kpidPackedSize4:
360      {
361        CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
362        if (folderIndex != kNumNoIndex)
363        {
364          const CFolder &folderInfo = _db.Folders[folderIndex];
365          if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2 &&
366              folderInfo.PackStreams.Size() > (int)(propID - kpidPackedSize0))
367          {
368            prop = _db.GetFolderPackStreamSize(folderIndex, propID - kpidPackedSize0);
369          }
370          else
371            prop = (UInt64)0;
372        }
373        else
374          prop = (UInt64)0;
375      }
376      break;
377    #endif
378  }
379  prop.Detach(value);
380  return S_OK;
381  COM_TRY_END
382}
383
384STDMETHODIMP CHandler::Open(IInStream *stream,
385    const UInt64 *maxCheckStartPosition,
386    IArchiveOpenCallback *openArchiveCallback)
387{
388  COM_TRY_BEGIN
389  Close();
390  #ifndef _SFX
391  _fileInfoPopIDs.Clear();
392  #endif
393  try
394  {
395    CMyComPtr<IArchiveOpenCallback> openArchiveCallbackTemp = openArchiveCallback;
396
397    #ifndef _NO_CRYPTO
398    CMyComPtr<ICryptoGetTextPassword> getTextPassword;
399    if (openArchiveCallback)
400    {
401      openArchiveCallbackTemp.QueryInterface(
402          IID_ICryptoGetTextPassword, &getTextPassword);
403    }
404    #endif
405    CInArchive archive;
406    RINOK(archive.Open(stream, maxCheckStartPosition));
407    #ifndef _NO_CRYPTO
408    _passwordIsDefined = false;
409    UString password;
410    #endif
411    HRESULT result = archive.ReadDatabase(
412      EXTERNAL_CODECS_VARS
413      _db
414      #ifndef _NO_CRYPTO
415      , getTextPassword, _passwordIsDefined
416      #endif
417      );
418    RINOK(result);
419    _db.Fill();
420    _inStream = stream;
421  }
422  catch(...)
423  {
424    Close();
425    return S_FALSE;
426  }
427  // _inStream = stream;
428  #ifndef _SFX
429  FillPopIDs();
430  #endif
431  return S_OK;
432  COM_TRY_END
433}
434
435STDMETHODIMP CHandler::Close()
436{
437  COM_TRY_BEGIN
438  _inStream.Release();
439  _db.Clear();
440  return S_OK;
441  COM_TRY_END
442}
443
444#ifdef __7Z_SET_PROPERTIES
445#ifdef EXTRACT_ONLY
446
447STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties)
448{
449  COM_TRY_BEGIN
450  const UInt32 numProcessors = NSystem::GetNumberOfProcessors();
451  _numThreads = numProcessors;
452
453  for (int i = 0; i < numProperties; i++)
454  {
455    UString name = names[i];
456    name.MakeUpper();
457    if (name.IsEmpty())
458      return E_INVALIDARG;
459    const PROPVARIANT &value = values[i];
460    UInt32 number;
461    int index = ParseStringToUInt32(name, number);
462    if (index == 0)
463    {
464      if(name.Left(2).CompareNoCase(L"MT") == 0)
465      {
466        RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads));
467        continue;
468      }
469      else
470        return E_INVALIDARG;
471    }
472  }
473  return S_OK;
474  COM_TRY_END
475}
476
477#endif
478#endif
479
480IMPL_ISetCompressCodecsInfo
481
482}}
483