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;
26using namespace NCOM;
27
28namespace NArchive {
29namespace N7z {
30
31CHandler::CHandler()
32{
33  #ifndef _NO_CRYPTO
34  _isEncrypted = false;
35  _passwordIsDefined = false;
36  #endif
37
38  #ifdef EXTRACT_ONLY
39
40  _crcSize = 4;
41
42  #ifdef __7Z_SET_PROPERTIES
43  _numThreads = NSystem::GetNumberOfProcessors();
44  _useMultiThreadMixer = true;
45  #endif
46
47  #endif
48}
49
50STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
51{
52  *numItems = _db.Files.Size();
53  return S_OK;
54}
55
56#ifdef _SFX
57
58IMP_IInArchive_ArcProps_NO_Table
59
60STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProps)
61{
62  *numProps = 0;
63  return S_OK;
64}
65
66STDMETHODIMP CHandler::GetPropertyInfo(UInt32 /* index */,
67      BSTR * /* name */, PROPID * /* propID */, VARTYPE * /* varType */)
68{
69  return E_NOTIMPL;
70}
71
72#else
73
74static const Byte kArcProps[] =
75{
76  kpidHeadersSize,
77  kpidMethod,
78  kpidSolid,
79  kpidNumBlocks
80  // , kpidIsTree
81};
82
83IMP_IInArchive_ArcProps
84
85static inline char GetHex(unsigned value)
86{
87  return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
88}
89
90static unsigned ConvertMethodIdToString_Back(char *s, UInt64 id)
91{
92  int len = 0;
93  do
94  {
95    s[--len] = GetHex((unsigned)id & 0xF); id >>= 4;
96    s[--len] = GetHex((unsigned)id & 0xF); id >>= 4;
97  }
98  while (id != 0);
99  return (unsigned)-len;
100}
101
102static void ConvertMethodIdToString(AString &res, UInt64 id)
103{
104  const unsigned kLen = 32;
105  char s[kLen];
106  unsigned len = kLen - 1;
107  s[len] = 0;
108  res += s + len - ConvertMethodIdToString_Back(s + len, id);
109}
110
111static unsigned GetStringForSizeValue(char *s, UInt32 val)
112{
113  unsigned i;
114  for (i = 0; i <= 31; i++)
115    if (((UInt32)1 << i) == val)
116    {
117      if (i < 10)
118      {
119        s[0] = (char)('0' + i);
120        s[1] = 0;
121        return 1;
122      }
123           if (i < 20) { s[0] = '1'; s[1] = (char)('0' + i - 10); }
124      else if (i < 30) { s[0] = '2'; s[1] = (char)('0' + i - 20); }
125      else             { s[0] = '3'; s[1] = (char)('0' + i - 30); }
126      s[2] = 0;
127      return 2;
128    }
129  char c = 'b';
130  if      ((val & ((1 << 20) - 1)) == 0) { val >>= 20; c = 'm'; }
131  else if ((val & ((1 << 10) - 1)) == 0) { val >>= 10; c = 'k'; }
132  ::ConvertUInt32ToString(val, s);
133  unsigned pos = MyStringLen(s);
134  s[pos++] = c;
135  s[pos] = 0;
136  return pos;
137}
138
139/*
140static inline void AddHexToString(UString &res, Byte value)
141{
142  res += GetHex((Byte)(value >> 4));
143  res += GetHex((Byte)(value & 0xF));
144}
145*/
146
147static char *AddProp32(char *s, const char *name, UInt32 v)
148{
149  *s++ = ':';
150  s = MyStpCpy(s, name);
151  ::ConvertUInt32ToString(v, s);
152  return s + MyStringLen(s);
153}
154
155void CHandler::AddMethodName(AString &s, UInt64 id)
156{
157  AString name;
158  FindMethod(EXTERNAL_CODECS_VARS id, name);
159  if (name.IsEmpty())
160    ConvertMethodIdToString(s, id);
161  else
162    s += name;
163}
164
165#endif
166
167STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
168{
169  #ifndef _SFX
170  COM_TRY_BEGIN
171  #endif
172  NCOM::CPropVariant prop;
173  switch (propID)
174  {
175    #ifndef _SFX
176    case kpidMethod:
177    {
178      AString s;
179      const CParsedMethods &pm = _db.ParsedMethods;
180      FOR_VECTOR (i, pm.IDs)
181      {
182        UInt64 id = pm.IDs[i];
183        s.Add_Space_if_NotEmpty();
184        char temp[16];
185        if (id == k_LZMA2)
186        {
187          s += "LZMA2:";
188          if ((pm.Lzma2Prop & 1) == 0)
189            ConvertUInt32ToString((pm.Lzma2Prop >> 1) + 12, temp);
190          else
191            GetStringForSizeValue(temp, 3 << ((pm.Lzma2Prop >> 1) + 11));
192          s += temp;
193        }
194        else if (id == k_LZMA)
195        {
196          s += "LZMA:";
197          GetStringForSizeValue(temp, pm.LzmaDic);
198          s += temp;
199        }
200        else
201          AddMethodName(s, id);
202      }
203      prop = s;
204      break;
205    }
206    case kpidSolid: prop = _db.IsSolid(); break;
207    case kpidNumBlocks: prop = (UInt32)_db.NumFolders; break;
208    case kpidHeadersSize:  prop = _db.HeadersSize; break;
209    case kpidPhySize:  prop = _db.PhySize; break;
210    case kpidOffset: if (_db.ArcInfo.StartPosition != 0) prop = _db.ArcInfo.StartPosition; break;
211    /*
212    case kpidIsTree: if (_db.IsTree) prop = true; break;
213    case kpidIsAltStream: if (_db.ThereAreAltStreams) prop = true; break;
214    case kpidIsAux: if (_db.IsTree) prop = true; break;
215    */
216    // case kpidError: if (_db.ThereIsHeaderError) prop = "Header error"; break;
217    #endif
218
219    case kpidWarningFlags:
220    {
221      UInt32 v = 0;
222      if (_db.StartHeaderWasRecovered) v |= kpv_ErrorFlags_HeadersError;
223      if (_db.UnsupportedFeatureWarning) v |= kpv_ErrorFlags_UnsupportedFeature;
224      if (v != 0)
225        prop = v;
226      break;
227    }
228
229    case kpidErrorFlags:
230    {
231      UInt32 v = 0;
232      if (!_db.IsArc) v |= kpv_ErrorFlags_IsNotArc;
233      if (_db.ThereIsHeaderError) v |= kpv_ErrorFlags_HeadersError;
234      if (_db.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd;
235      // if (_db.UnsupportedVersion) v |= kpv_ErrorFlags_Unsupported;
236      if (_db.UnsupportedFeatureError) v |= kpv_ErrorFlags_UnsupportedFeature;
237      prop = v;
238      break;
239    }
240  }
241  prop.Detach(value);
242  return S_OK;
243  #ifndef _SFX
244  COM_TRY_END
245  #endif
246}
247
248static void SetFileTimeProp_From_UInt64Def(PROPVARIANT *prop, const CUInt64DefVector &v, int index)
249{
250  UInt64 value;
251  if (v.GetItem(index, value))
252    PropVarEm_Set_FileTime64(prop, value);
253}
254
255bool CHandler::IsFolderEncrypted(CNum folderIndex) const
256{
257  if (folderIndex == kNumNoIndex)
258    return false;
259  size_t startPos = _db.FoCodersDataOffset[folderIndex];
260  const Byte *p = _db.CodersData + startPos;
261  size_t size = _db.FoCodersDataOffset[folderIndex + 1] - startPos;
262  CInByte2 inByte;
263  inByte.Init(p, size);
264
265  CNum numCoders = inByte.ReadNum();
266  for (; numCoders != 0; numCoders--)
267  {
268    Byte mainByte = inByte.ReadByte();
269    unsigned idSize = (mainByte & 0xF);
270    const Byte *longID = inByte.GetPtr();
271    UInt64 id64 = 0;
272    for (unsigned j = 0; j < idSize; j++)
273      id64 = ((id64 << 8) | longID[j]);
274    inByte.SkipDataNoCheck(idSize);
275    if (id64 == k_AES)
276      return true;
277    if ((mainByte & 0x20) != 0)
278      inByte.SkipDataNoCheck(inByte.ReadNum());
279  }
280  return false;
281}
282
283STDMETHODIMP CHandler::GetNumRawProps(UInt32 *numProps)
284{
285  *numProps = 0;
286  return S_OK;
287}
288
289STDMETHODIMP CHandler::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID)
290{
291  *name = NULL;
292  *propID = kpidNtSecure;
293  return S_OK;
294}
295
296STDMETHODIMP CHandler::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType)
297{
298  /*
299  const CFileItem &file = _db.Files[index];
300  *parentType = (file.IsAltStream ? NParentType::kAltStream : NParentType::kDir);
301  *parent = (UInt32)(Int32)file.Parent;
302  */
303  *parentType = NParentType::kDir;
304  *parent = (UInt32)(Int32)-1;
305  return S_OK;
306}
307
308STDMETHODIMP CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
309{
310  *data = NULL;
311  *dataSize = 0;
312  *propType = 0;
313
314  if (/* _db.IsTree && propID == kpidName ||
315      !_db.IsTree && */ propID == kpidPath)
316  {
317    if (_db.NameOffsets && _db.NamesBuf)
318    {
319      size_t offset = _db.NameOffsets[index];
320      size_t size = (_db.NameOffsets[index + 1] - offset) * 2;
321      if (size < ((UInt32)1 << 31))
322      {
323        *data = (const void *)(_db.NamesBuf + offset * 2);
324        *dataSize = (UInt32)size;
325        *propType = NPropDataType::kUtf16z;
326      }
327    }
328    return S_OK;
329  }
330  /*
331  if (propID == kpidNtSecure)
332  {
333    if (index < (UInt32)_db.SecureIDs.Size())
334    {
335      int id = _db.SecureIDs[index];
336      size_t offs = _db.SecureOffsets[id];
337      size_t size = _db.SecureOffsets[id + 1] - offs;
338      if (size >= 0)
339      {
340        *data = _db.SecureBuf + offs;
341        *dataSize = (UInt32)size;
342        *propType = NPropDataType::kRaw;
343      }
344    }
345  }
346  */
347  return S_OK;
348}
349
350#ifndef _SFX
351
352HRESULT CHandler::SetMethodToProp(CNum folderIndex, PROPVARIANT *prop) const
353{
354  PropVariant_Clear(prop);
355  if (folderIndex == kNumNoIndex)
356    return S_OK;
357  // for (int ttt = 0; ttt < 1; ttt++) {
358  const unsigned kTempSize = 256;
359  char temp[kTempSize];
360  unsigned pos = kTempSize;
361  temp[--pos] = 0;
362
363  size_t startPos = _db.FoCodersDataOffset[folderIndex];
364  const Byte *p = _db.CodersData + startPos;
365  size_t size = _db.FoCodersDataOffset[folderIndex + 1] - startPos;
366  CInByte2 inByte;
367  inByte.Init(p, size);
368
369  // numCoders == 0 ???
370  CNum numCoders = inByte.ReadNum();
371  bool needSpace = false;
372
373  for (; numCoders != 0; numCoders--, needSpace = true)
374  {
375    if (pos < 32) // max size of property
376      break;
377    Byte mainByte = inByte.ReadByte();
378    unsigned idSize = (mainByte & 0xF);
379    const Byte *longID = inByte.GetPtr();
380    UInt64 id64 = 0;
381    for (unsigned j = 0; j < idSize; j++)
382      id64 = ((id64 << 8) | longID[j]);
383    inByte.SkipDataNoCheck(idSize);
384
385    if ((mainByte & 0x10) != 0)
386    {
387      inByte.ReadNum(); // NumInStreams
388      inByte.ReadNum(); // NumOutStreams
389    }
390
391    CNum propsSize = 0;
392    const Byte *props = NULL;
393    if ((mainByte & 0x20) != 0)
394    {
395      propsSize = inByte.ReadNum();
396      props = inByte.GetPtr();
397      inByte.SkipDataNoCheck(propsSize);
398    }
399
400    const char *name = NULL;
401    char s[32];
402    s[0] = 0;
403
404    if (id64 <= (UInt32)0xFFFFFFFF)
405    {
406      UInt32 id = (UInt32)id64;
407      if (id == k_LZMA)
408      {
409        name = "LZMA";
410        if (propsSize == 5)
411        {
412          UInt32 dicSize = GetUi32((const Byte *)props + 1);
413          char *dest = s + GetStringForSizeValue(s, dicSize);
414          UInt32 d = props[0];
415          if (d != 0x5D)
416          {
417            UInt32 lc = d % 9;
418            d /= 9;
419            UInt32 pb = d / 5;
420            UInt32 lp = d % 5;
421            if (lc != 3) dest = AddProp32(dest, "lc", lc);
422            if (lp != 0) dest = AddProp32(dest, "lp", lp);
423            if (pb != 2) dest = AddProp32(dest, "pb", pb);
424          }
425        }
426      }
427      else if (id == k_LZMA2)
428      {
429        name = "LZMA2";
430        if (propsSize == 1)
431        {
432          Byte d = props[0];
433          if ((d & 1) == 0)
434            ConvertUInt32ToString((UInt32)((d >> 1) + 12), s);
435          else
436            GetStringForSizeValue(s, 3 << ((d >> 1) + 11));
437        }
438      }
439      else if (id == k_PPMD)
440      {
441        name = "PPMD";
442        if (propsSize == 5)
443        {
444          Byte order = *props;
445          char *dest = s;
446          *dest++ = 'o';
447          ConvertUInt32ToString(order, dest);
448          dest += MyStringLen(dest);
449          dest = MyStpCpy(dest, ":mem");
450          GetStringForSizeValue(dest, GetUi32(props + 1));
451        }
452      }
453      else if (id == k_Delta)
454      {
455        name = "Delta";
456        if (propsSize == 1)
457          ConvertUInt32ToString((UInt32)props[0] + 1, s);
458      }
459      else if (id == k_BCJ2) name = "BCJ2";
460      else if (id == k_BCJ) name = "BCJ";
461      else if (id == k_AES)
462      {
463        name = "7zAES";
464        if (propsSize >= 1)
465        {
466          Byte firstByte = props[0];
467          UInt32 numCyclesPower = firstByte & 0x3F;
468          ConvertUInt32ToString(numCyclesPower, s);
469        }
470      }
471    }
472
473    if (name)
474    {
475      unsigned nameLen = MyStringLen(name);
476      unsigned propsLen = MyStringLen(s);
477      unsigned totalLen = nameLen + propsLen;
478      if (propsLen != 0)
479        totalLen++;
480      if (needSpace)
481        totalLen++;
482      if (totalLen + 5 >= pos)
483        break;
484      pos -= totalLen;
485      MyStringCopy(temp + pos, name);
486      if (propsLen != 0)
487      {
488        char *dest = temp + pos + nameLen;
489        *dest++ = ':';
490        MyStringCopy(dest, s);
491      }
492      if (needSpace)
493        temp[pos + totalLen - 1] = ' ';
494    }
495    else
496    {
497      AString methodName;
498      FindMethod(EXTERNAL_CODECS_VARS id64, methodName);
499      if (needSpace)
500        temp[--pos] = ' ';
501      if (methodName.IsEmpty())
502        pos -= ConvertMethodIdToString_Back(temp + pos, id64);
503      else
504      {
505        unsigned len = methodName.Len();
506        if (len + 5 > pos)
507          break;
508        pos -= len;
509        for (unsigned i = 0; i < len; i++)
510          temp[pos + i] = methodName[i];
511      }
512    }
513  }
514
515  if (numCoders != 0 && pos >= 4)
516  {
517    temp[--pos] = ' ';
518    temp[--pos] = '.';
519    temp[--pos] = '.';
520    temp[--pos] = '.';
521  }
522
523  return PropVarEm_Set_Str(prop, temp + pos);
524  // }
525}
526
527#endif
528
529STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
530{
531  PropVariant_Clear(value);
532  // COM_TRY_BEGIN
533  // NCOM::CPropVariant prop;
534
535  /*
536  const CRef2 &ref2 = _refs[index];
537  if (ref2.Refs.IsEmpty())
538    return E_FAIL;
539  const CRef &ref = ref2.Refs.Front();
540  */
541
542  const CFileItem &item = _db.Files[index];
543  UInt32 index2 = index;
544
545  switch (propID)
546  {
547    case kpidIsDir: PropVarEm_Set_Bool(value, item.IsDir); break;
548    case kpidSize:
549    {
550      PropVarEm_Set_UInt64(value, item.Size);
551      // prop = ref2.Size;
552      break;
553    }
554    case kpidPackSize:
555    {
556      // prop = ref2.PackSize;
557      {
558        CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
559        if (folderIndex != kNumNoIndex)
560        {
561          if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2)
562            PropVarEm_Set_UInt64(value, _db.GetFolderFullPackSize(folderIndex));
563          /*
564          else
565            PropVarEm_Set_UInt64(value, 0);
566          */
567        }
568        else
569          PropVarEm_Set_UInt64(value, 0);
570      }
571      break;
572    }
573    // case kpidIsAux: prop = _db.IsItemAux(index2); break;
574    case kpidPosition:  { UInt64 v; if (_db.StartPos.GetItem(index2, v)) PropVarEm_Set_UInt64(value, v); break; }
575    case kpidCTime:  SetFileTimeProp_From_UInt64Def(value, _db.CTime, index2); break;
576    case kpidATime:  SetFileTimeProp_From_UInt64Def(value, _db.ATime, index2); break;
577    case kpidMTime:  SetFileTimeProp_From_UInt64Def(value, _db.MTime, index2); break;
578    case kpidAttrib:  if (item.AttribDefined) PropVarEm_Set_UInt32(value, item.Attrib); break;
579    case kpidCRC:  if (item.CrcDefined) PropVarEm_Set_UInt32(value, item.Crc); break;
580    case kpidEncrypted:  PropVarEm_Set_Bool(value, IsFolderEncrypted(_db.FileIndexToFolderIndexMap[index2])); break;
581    case kpidIsAnti:  PropVarEm_Set_Bool(value, _db.IsItemAnti(index2)); break;
582    /*
583    case kpidIsAltStream:  prop = item.IsAltStream; break;
584    case kpidNtSecure:
585      {
586        int id = _db.SecureIDs[index];
587        size_t offs = _db.SecureOffsets[id];
588        size_t size = _db.SecureOffsets[id + 1] - offs;
589        if (size >= 0)
590        {
591          prop.SetBlob(_db.SecureBuf + offs, (ULONG)size);
592        }
593        break;
594      }
595    */
596
597    case kpidPath: return _db.GetPath_Prop(index, value);
598
599    #ifndef _SFX
600
601    case kpidMethod: return SetMethodToProp(_db.FileIndexToFolderIndexMap[index2], value);
602    case kpidBlock:
603      {
604        CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
605        if (folderIndex != kNumNoIndex)
606          PropVarEm_Set_UInt32(value, (UInt32)folderIndex);
607      }
608      break;
609    /*
610    case kpidPackedSize0:
611    case kpidPackedSize1:
612    case kpidPackedSize2:
613    case kpidPackedSize3:
614    case kpidPackedSize4:
615      {
616        CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
617        if (folderIndex != kNumNoIndex)
618        {
619          if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2 &&
620              _db.FoStartPackStreamIndex[folderIndex + 1] -
621              _db.FoStartPackStreamIndex[folderIndex] > (propID - kpidPackedSize0))
622          {
623            PropVarEm_Set_UInt64(value, _db.GetFolderPackStreamSize(folderIndex, propID - kpidPackedSize0));
624          }
625        }
626        else
627          PropVarEm_Set_UInt64(value, 0);
628      }
629      break;
630    */
631
632    #endif
633  }
634  // prop.Detach(value);
635  return S_OK;
636  // COM_TRY_END
637}
638
639STDMETHODIMP CHandler::Open(IInStream *stream,
640    const UInt64 *maxCheckStartPosition,
641    IArchiveOpenCallback *openArchiveCallback)
642{
643  COM_TRY_BEGIN
644  Close();
645  #ifndef _SFX
646  _fileInfoPopIDs.Clear();
647  #endif
648
649  try
650  {
651    CMyComPtr<IArchiveOpenCallback> openArchiveCallbackTemp = openArchiveCallback;
652
653    #ifndef _NO_CRYPTO
654    CMyComPtr<ICryptoGetTextPassword> getTextPassword;
655    if (openArchiveCallback)
656      openArchiveCallbackTemp.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
657    #endif
658
659    CInArchive archive(
660          #ifdef __7Z_SET_PROPERTIES
661          _useMultiThreadMixer
662          #else
663          true
664          #endif
665          );
666    _db.IsArc = false;
667    RINOK(archive.Open(stream, maxCheckStartPosition));
668    _db.IsArc = true;
669
670    HRESULT result = archive.ReadDatabase(
671        EXTERNAL_CODECS_VARS
672        _db
673        #ifndef _NO_CRYPTO
674          , getTextPassword, _isEncrypted, _passwordIsDefined, _password
675        #endif
676        );
677    RINOK(result);
678
679    _inStream = stream;
680  }
681  catch(...)
682  {
683    Close();
684    // return E_INVALIDARG;
685    // return S_FALSE;
686    // we must return out_of_memory here
687    return E_OUTOFMEMORY;
688  }
689  // _inStream = stream;
690  #ifndef _SFX
691  FillPopIDs();
692  #endif
693  return S_OK;
694  COM_TRY_END
695}
696
697STDMETHODIMP CHandler::Close()
698{
699  COM_TRY_BEGIN
700  _inStream.Release();
701  _db.Clear();
702  #ifndef _NO_CRYPTO
703  _isEncrypted = false;
704  _passwordIsDefined = false;
705  _password.Empty();
706  #endif
707  return S_OK;
708  COM_TRY_END
709}
710
711#ifdef __7Z_SET_PROPERTIES
712#ifdef EXTRACT_ONLY
713
714STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
715{
716  COM_TRY_BEGIN
717  const UInt32 numProcessors = NSystem::GetNumberOfProcessors();
718  _numThreads = numProcessors;
719  _useMultiThreadMixer = true;
720
721  for (UInt32 i = 0; i < numProps; i++)
722  {
723    UString name = names[i];
724    name.MakeLower_Ascii();
725    if (name.IsEmpty())
726      return E_INVALIDARG;
727    const PROPVARIANT &value = values[i];
728    UInt32 number;
729    unsigned index = ParseStringToUInt32(name, number);
730    if (index == 0)
731    {
732      if (name.IsEqualTo("mtf"))
733      {
734        RINOK(PROPVARIANT_to_bool(value, _useMultiThreadMixer));
735        continue;
736      }
737      if (name.IsPrefixedBy_Ascii_NoCase("mt"))
738      {
739        RINOK(ParseMtProp(name.Ptr(2), value, numProcessors, _numThreads));
740        continue;
741      }
742      else
743        return E_INVALIDARG;
744    }
745  }
746  return S_OK;
747  COM_TRY_END
748}
749
750#endif
751#endif
752
753IMPL_ISetCompressCodecsInfo
754
755}}
756