1// 7zUpdate.cpp
2
3#include "StdAfx.h"
4
5#include "../../../../C/CpuArch.h"
6
7#include "../../../Common/MyLinux.h"
8#include "../../../Common/StringToInt.h"
9#include "../../../Common/Wildcard.h"
10
11#include "../../Common/CreateCoder.h"
12#include "../../Common/LimitedStreams.h"
13#include "../../Common/ProgressUtils.h"
14
15#include "../../Compress/CopyCoder.h"
16
17#include "../Common/ItemNameUtils.h"
18
19#include "7zDecode.h"
20#include "7zEncode.h"
21#include "7zFolderInStream.h"
22#include "7zHandler.h"
23#include "7zOut.h"
24#include "7zUpdate.h"
25
26namespace NArchive {
27namespace N7z {
28
29#define k_X86 k_BCJ
30
31struct CFilterMode
32{
33  UInt32 Id;
34  UInt32 Delta;  // required File Size alignment, if Id is not k_Delta.
35                 // (Delta == 0) means unknown alignment
36  UInt32 Offset; // for k_ARM64
37  // UInt32 AlignSizeOpt; // for k_ARM64
38
39  CFilterMode():
40    Id(0),
41    Delta(0),
42    Offset(0)
43    // , AlignSizeOpt(0)
44    {}
45
46  void ClearFilterMode()
47  {
48    Id = 0;
49    Delta = 0;
50    Offset = 0;
51    // AlignSizeOpt = 0;
52  }
53
54  // it sets Delta as Align value, if Id is exe filter
55  // in another cases it sets Delta = 0, that
56  void SetDelta()
57  {
58    if (Id == k_IA64)
59      Delta = 16;
60    else if (Id == k_ARM64 || Id == k_ARM || Id == k_PPC || Id == k_SPARC)
61      Delta = 4;
62    else if (Id == k_ARMT)
63      Delta = 2;
64    else if (Id == k_BCJ || Id == k_BCJ2)
65      Delta = 1; // do we need it?
66    else
67      Delta = 0;
68  }
69};
70
71
72/* ---------- PE ---------- */
73
74#define MZ_SIG 0x5A4D
75
76#define PE_SIG 0x00004550
77#define PE_OptHeader_Magic_32 0x10B
78#define PE_OptHeader_Magic_64 0x20B
79// #define PE_SectHeaderSize 40
80// #define PE_SECT_EXECUTE 0x20000000
81
82static int Parse_EXE(const Byte *buf, size_t size, CFilterMode *filterMode)
83{
84  if (size < 512 || GetUi16(buf) != MZ_SIG)
85    return 0;
86
87  const Byte *p;
88  UInt32 peOffset, optHeaderSize, filterId;
89
90  peOffset = GetUi32(buf + 0x3C);
91  if (peOffset >= 0x1000 || peOffset + 512 > size || (peOffset & 7) != 0)
92    return 0;
93  p = buf + peOffset;
94  if (GetUi32(p) != PE_SIG)
95    return 0;
96  p += 4;
97
98  switch (GetUi16(p))
99  {
100    case 0x014C:
101    case 0x8664:  filterId = k_X86; break;
102    case 0xAA64:  filterId = k_ARM64; break;
103
104    /*
105    IMAGE_FILE_MACHINE_ARM   0x01C0  // ARM LE
106    IMAGE_FILE_MACHINE_THUMB 0x01C2  // ARM Thumb / Thumb-2 LE
107    IMAGE_FILE_MACHINE_ARMNT 0x01C4  // ARM Thumb-2, LE
108    Note: We use ARM filter for 0x01C2. (WinCE 5 - 0x01C2) files mostly contain ARM code (not Thumb/Thumb-2).
109    */
110
111    case 0x01C0:                            // WinCE old
112    case 0x01C2:  filterId = k_ARM; break;  // WinCE new
113    case 0x01C4:  filterId = k_ARMT; break; // WinRT
114
115    case 0x0200:  filterId = k_IA64; break;
116    default:  return 0;
117  }
118
119  // const UInt32 numSections = GetUi16(p + 2);
120  optHeaderSize = GetUi16(p + 16);
121  if (optHeaderSize > (1 << 10))
122    return 0;
123
124  p += 20; /* headerSize */
125
126  switch (GetUi16(p))
127  {
128    case PE_OptHeader_Magic_32:
129    case PE_OptHeader_Magic_64:
130      break;
131    default:
132      return 0;
133  }
134
135  /*
136    // Windows exe file sizes are not aligned for 4 KiB.
137    // So we can't use (CFilterMode::Offset != 0) in solid archives.
138    // So we just don't set Offset here.
139#define NUM_SCAN_SECTIONS_MAX (1 << 6)
140#define EXE_SECTION_OFFSET_MAX (1 << 27)
141#define EXE_SECTION_SIZE_MIN (1 << 8)
142#define EXE_SECTION_SIZE_MAX (1 << 27)
143#define PE_SectHeaderSize 40
144#define PE_SECT_EXECUTE 0x20000000
145
146  if (numSections > NUM_SCAN_SECTIONS_MAX)
147    return 0;
148
149  p += optHeaderSize;
150  // UInt32 numExeSections = 0;
151  // bool execute_finded = false;
152  // UInt32 sect_va = 0;
153  // UInt32 sect_size = 0;
154  // UInt32 sect_offset = 0;
155
156  for (UInt32 i = 0; i < numSections
157        // && numExeSections < numSectionsMax
158        ; i++, p += PE_SectHeaderSize)
159  {
160    UInt32 characts, rawSize, offset;
161    if ((UInt32)(p - buf) + PE_SectHeaderSize > size)
162      return 0;
163    rawSize = GetUi32(p + 16);
164    offset = GetUi32(p + 20);
165    characts = GetUi32(p + 36);
166    if (rawSize >= EXE_SECTION_SIZE_MIN &&
167        rawSize <= EXE_SECTION_SIZE_MAX &&
168        offset <= EXE_SECTION_OFFSET_MAX &&
169        // offset < limit &&
170        offset > 0)
171    {
172      if ((characts & PE_SECT_EXECUTE) != 0)
173      {
174        // execute_finded = true;
175        // sect_va = GetUi32(p + 12);
176        // sect_size = rawSize;
177        // sect_offset = offset;
178        break;
179      }
180    }
181  }
182
183  filterMode->Offset = 0;
184  if (filterId == k_ARM64)
185  {
186    // filterMode->AlignSizeOpt = (1 << 12);
187    // const UInt32 offs = (sect_va - sect_offset) & 0xFFF;
188    // if (offs != 0)
189    // filterMode->Offset = offs; // change it
190  }
191  */
192  filterMode->Id = filterId;
193  return 1;
194}
195
196
197/* ---------- ELF ---------- */
198
199#define ELF_SIG 0x464C457F
200
201#define ELF_CLASS_32  1
202#define ELF_CLASS_64  2
203
204#define ELF_DATA_2LSB 1
205#define ELF_DATA_2MSB 2
206
207static UInt16 Get16(const Byte *p, BoolInt be) { if (be) return (UInt16)GetBe16(p); return (UInt16)GetUi16(p); }
208static UInt32 Get32(const Byte *p, BoolInt be) { if (be) return GetBe32(p); return GetUi32(p); }
209// static UInt64 Get64(const Byte *p, BoolInt be) { if (be) return GetBe64(p); return GetUi64(p); }
210
211static int Parse_ELF(const Byte *buf, size_t size, CFilterMode *filterMode)
212{
213  BoolInt /* is32, */ be;
214  UInt32 filterId;
215
216  if (size < 512 || buf[6] != 1) /* ver */
217    return 0;
218
219  if (GetUi32(buf) != ELF_SIG)
220    return 0;
221
222  switch (buf[4])
223  {
224    case ELF_CLASS_32: /* is32 = True; */ break;
225    case ELF_CLASS_64: /* is32 = False; */ break;
226    default: return 0;
227  }
228
229  switch (buf[5])
230  {
231    case ELF_DATA_2LSB: be = False; break;
232    case ELF_DATA_2MSB: be = True; break;
233    default: return 0;
234  }
235
236  switch (Get16(buf + 0x12, be))
237  {
238    case 3:
239    case 6:
240    case 62: filterId = k_X86; break;
241    case 2:
242    case 18:
243    case 43: filterId = k_SPARC; break;
244    case 20:
245    case 21: if (!be) return 0; filterId = k_PPC; break;
246    case 40: if ( be) return 0; filterId = k_ARM; break;
247    case 183: if (be) return 0; filterId = k_ARM64; break;
248
249    /* Some IA-64 ELF executables have size that is not aligned for 16 bytes.
250       So we don't use IA-64 filter for IA-64 ELF */
251    // case 50: if ( be) return 0; filterId = k_IA64; break;
252
253    default: return 0;
254  }
255
256  filterMode->Id = filterId;
257  return 1;
258}
259
260
261
262/* ---------- Mach-O ---------- */
263
264#define MACH_SIG_BE_32 0xCEFAEDFE
265#define MACH_SIG_BE_64 0xCFFAEDFE
266#define MACH_SIG_LE_32 0xFEEDFACE
267#define MACH_SIG_LE_64 0xFEEDFACF
268
269#define MACH_ARCH_ABI64 (1 << 24)
270#define MACH_MACHINE_386 7
271#define MACH_MACHINE_ARM 12
272#define MACH_MACHINE_SPARC 14
273#define MACH_MACHINE_PPC 18
274#define MACH_MACHINE_PPC64 (MACH_ARCH_ABI64 | MACH_MACHINE_PPC)
275#define MACH_MACHINE_AMD64 (MACH_ARCH_ABI64 | MACH_MACHINE_386)
276#define MACH_MACHINE_ARM64 (MACH_ARCH_ABI64 | MACH_MACHINE_ARM)
277
278static unsigned Parse_MACH(const Byte *buf, size_t size, CFilterMode *filterMode)
279{
280  UInt32 filterId, numCommands, commandsSize;
281
282  if (size < 512)
283    return 0;
284
285  BoolInt /* mode64, */ be;
286  switch (GetUi32(buf))
287  {
288    case MACH_SIG_BE_32: /* mode64 = False; */ be = True; break;
289    case MACH_SIG_BE_64: /* mode64 = True;  */ be = True; break;
290    case MACH_SIG_LE_32: /* mode64 = False; */ be = False; break;
291    case MACH_SIG_LE_64: /* mode64 = True;  */ be = False; break;
292    default: return 0;
293  }
294
295  switch (Get32(buf + 4, be))
296  {
297    case MACH_MACHINE_386:
298    case MACH_MACHINE_AMD64: filterId = k_X86; break;
299    case MACH_MACHINE_ARM:   if ( be) return 0; filterId = k_ARM; break;
300    case MACH_MACHINE_SPARC: if (!be) return 0; filterId = k_SPARC; break;
301    case MACH_MACHINE_PPC:
302    case MACH_MACHINE_PPC64: if (!be) return 0; filterId = k_PPC; break;
303    case MACH_MACHINE_ARM64: if ( be) return 0; filterId = k_ARM64; break;
304    default: return 0;
305  }
306
307  numCommands = Get32(buf + 0x10, be);
308  commandsSize = Get32(buf + 0x14, be);
309
310  if (commandsSize > (1 << 24) || numCommands > (1 << 18))
311    return 0;
312
313  filterMode->Id = filterId;
314  return 1;
315}
316
317
318/* ---------- WAV ---------- */
319
320#define WAV_SUBCHUNK_fmt  0x20746D66
321#define WAV_SUBCHUNK_data 0x61746164
322
323#define RIFF_SIG 0x46464952
324
325static BoolInt Parse_WAV(const Byte *buf, size_t size, CFilterMode *filterMode)
326{
327  UInt32 subChunkSize, pos;
328  if (size < 0x2C)
329    return False;
330
331  if (GetUi32(buf + 0) != RIFF_SIG ||
332      GetUi32(buf + 8) != 0x45564157 || // WAVE
333      GetUi32(buf + 0xC) != WAV_SUBCHUNK_fmt)
334    return False;
335  subChunkSize = GetUi32(buf + 0x10);
336  /* [0x14 = format] = 1 (PCM) */
337  if (subChunkSize < 0x10 || subChunkSize > 0x12 || GetUi16(buf + 0x14) != 1)
338    return False;
339
340  const unsigned numChannels = GetUi16(buf + 0x16);
341  const unsigned bitsPerSample = GetUi16(buf + 0x22);
342  if ((bitsPerSample & 0x7) != 0)
343    return False;
344  const UInt32 delta = (UInt32)numChannels * (bitsPerSample >> 3);
345  if (delta == 0 || delta > 256)
346    return False;
347
348  pos = 0x14 + subChunkSize;
349
350  const int kNumSubChunksTests = 10;
351  // Do we need to scan more than 3 sub-chunks?
352  for (int i = 0; i < kNumSubChunksTests; i++)
353  {
354    if (pos + 8 > size)
355      return False;
356    subChunkSize = GetUi32(buf + pos + 4);
357    if (GetUi32(buf + pos) == WAV_SUBCHUNK_data)
358    {
359      filterMode->Id = k_Delta;
360      filterMode->Delta = delta;
361      return True;
362    }
363    if (subChunkSize > (1 << 16))
364      return False;
365    pos += subChunkSize + 8;
366  }
367  return False;
368}
369
370
371/*
372  filterMode->Delta will be set as:
373    = delta value : [1, 256] : for k_Delta
374    = 0 for another filters (branch filters)
375*/
376static BoolInt ParseFile(const Byte *buf, size_t size, CFilterMode *filterMode)
377{
378  filterMode->ClearFilterMode();
379
380  if (Parse_EXE(buf, size, filterMode)) return True;
381  if (Parse_ELF(buf, size, filterMode)) return True;
382  if (Parse_MACH(buf, size, filterMode)) return True;
383  return Parse_WAV(buf, size, filterMode);
384}
385
386
387
388
389struct CFilterMode2: public CFilterMode
390{
391  bool Encrypted;
392  unsigned GroupIndex;
393
394  CFilterMode2(): Encrypted(false) {}
395
396  int Compare(const CFilterMode2 &m) const
397  {
398    if (!Encrypted)
399    {
400      if (m.Encrypted)
401        return -1;
402    }
403    else if (!m.Encrypted)
404      return 1;
405
406    const UInt32 id1 = Id;
407    const UInt32 id2 = m.Id;
408    /*
409    // we can change the order to place k_ARM64 files close to another exe files
410    if (id1 <= k_SPARC &&
411        id2 <= k_SPARC)
412    {
413      #define k_ARM64_FOR_SORT 0x3030901
414      if (id1 == k_ARM64) id1 = k_ARM64_FOR_SORT;
415      if (id2 == k_ARM64) id2 = k_ARM64_FOR_SORT;
416    }
417    */
418    if (id1 < id2) return -1;
419    if (id1 > id2) return 1;
420
421    if (Delta < m.Delta) return -1;
422    if (Delta > m.Delta) return 1;
423
424    if (Offset < m.Offset) return -1;
425    if (Offset > m.Offset) return 1;
426
427    /* we don't go here, because GetGroup()
428       and operator ==(const CFilterMode2 &m)
429       add only unique CFilterMode2:: { Id, Delta, Offset, Encrypted } items.
430    */
431    /*
432    if (GroupIndex < m.GroupIndex) return -1;
433    if (GroupIndex > m.GroupIndex) return 1;
434    */
435    return 0;
436  }
437
438  bool operator ==(const CFilterMode2 &m) const
439  {
440    return Id == m.Id
441        && Delta == m.Delta
442        && Offset == m.Offset
443        && Encrypted == m.Encrypted;
444  }
445};
446
447static unsigned GetGroup(CRecordVector<CFilterMode2> &filters, const CFilterMode2 &m)
448{
449  unsigned i;
450  for (i = 0; i < filters.Size(); i++)
451  {
452    const CFilterMode2 &m2 = filters[i];
453    if (m == m2)
454      return i;
455    /*
456    if (m.Encrypted != m2.Encrypted)
457    {
458      if (!m.Encrypted)
459        break;
460      continue;
461    }
462
463    if (m.Id < m2.Id)  break;
464    if (m.Id != m2.Id) continue;
465
466    if (m.Delta < m2.Delta) break;
467    if (m.Delta != m2.Delta) continue;
468    */
469  }
470  // filters.Insert(i, m);
471  // return i;
472  return filters.Add(m);
473}
474
475static inline bool Is86Filter(CMethodId m)
476{
477  return (m == k_BCJ || m == k_BCJ2);
478}
479
480static inline bool IsExeFilter(CMethodId m)
481{
482  switch (m)
483  {
484    case k_ARM64:
485    case k_BCJ:
486    case k_BCJ2:
487    case k_ARM:
488    case k_ARMT:
489    case k_PPC:
490    case k_SPARC:
491    case k_IA64:
492      return true;
493  }
494  return false;
495}
496
497static unsigned Get_FilterGroup_for_Folder(
498    CRecordVector<CFilterMode2> &filters, const CFolderEx &f, bool extractFilter)
499{
500  CFilterMode2 m;
501  // m.Id = 0;
502  // m.Delta = 0;
503  // m.Offset = 0;
504  m.Encrypted = f.IsEncrypted();
505
506  if (extractFilter)
507  {
508    const CCoderInfo &coder = f.Coders[f.UnpackCoder];
509
510    if (coder.MethodID == k_Delta)
511    {
512      if (coder.Props.Size() == 1)
513      {
514        m.Delta = (unsigned)coder.Props[0] + 1;
515        m.Id = k_Delta;
516      }
517    }
518    else if (IsExeFilter(coder.MethodID))
519    {
520      m.Id = (UInt32)coder.MethodID;
521      if (m.Id == k_BCJ2)
522        m.Id = k_BCJ;
523      m.SetDelta();
524      if (m.Id == k_ARM64)
525        if (coder.Props.Size() == 4)
526          m.Offset = GetUi32(coder.Props);
527    }
528  }
529
530  return GetGroup(filters, m);
531}
532
533
534
535
536static HRESULT WriteRange(IInStream *inStream, ISequentialOutStream *outStream,
537    UInt64 position, UInt64 size, ICompressProgressInfo *progress)
538{
539  RINOK(InStream_SeekSet(inStream, position))
540  CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
541  CMyComPtr<ISequentialInStream> inStreamLimited(streamSpec);
542  streamSpec->SetStream(inStream);
543  streamSpec->Init(size);
544
545  NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
546  CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
547  RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress))
548  return (copyCoderSpec->TotalSize == size ? S_OK : E_FAIL);
549}
550
551/*
552unsigned CUpdateItem::GetExtensionPos() const
553{
554  int slashPos = Name.ReverseFind_PathSepar();
555  int dotPos = Name.ReverseFind_Dot();
556  if (dotPos <= slashPos)
557    return Name.Len();
558  return dotPos + 1;
559}
560
561UString CUpdateItem::GetExtension() const
562{
563  return Name.Ptr(GetExtensionPos());
564}
565*/
566
567#define RINOZ(x) { const int _t_ = (x); if (_t_ != 0) return _t_; }
568
569#define RINOZ_COMP(a, b) RINOZ(MyCompare(a, b))
570
571/*
572static int CompareBuffers(const CByteBuffer &a1, const CByteBuffer &a2)
573{
574  size_t c1 = a1.GetCapacity();
575  size_t c2 = a2.GetCapacity();
576  RINOZ_COMP(c1, c2);
577  for (size_t i = 0; i < c1; i++)
578    RINOZ_COMP(a1[i], a2[i]);
579  return 0;
580}
581
582static int CompareCoders(const CCoderInfo &c1, const CCoderInfo &c2)
583{
584  RINOZ_COMP(c1.NumInStreams, c2.NumInStreams);
585  RINOZ_COMP(c1.NumOutStreams, c2.NumOutStreams);
586  RINOZ_COMP(c1.MethodID, c2.MethodID);
587  return CompareBuffers(c1.Props, c2.Props);
588}
589
590static int CompareBonds(const CBond &b1, const CBond &b2)
591{
592  RINOZ_COMP(b1.InIndex, b2.InIndex);
593  return MyCompare(b1.OutIndex, b2.OutIndex);
594}
595
596static int CompareFolders(const CFolder &f1, const CFolder &f2)
597{
598  int s1 = f1.Coders.Size();
599  int s2 = f2.Coders.Size();
600  RINOZ_COMP(s1, s2);
601  int i;
602  for (i = 0; i < s1; i++)
603    RINOZ(CompareCoders(f1.Coders[i], f2.Coders[i]));
604  s1 = f1.Bonds.Size();
605  s2 = f2.Bonds.Size();
606  RINOZ_COMP(s1, s2);
607  for (i = 0; i < s1; i++)
608    RINOZ(CompareBonds(f1.Bonds[i], f2.Bonds[i]));
609  return 0;
610}
611*/
612
613/*
614static int CompareFiles(const CFileItem &f1, const CFileItem &f2)
615{
616  return CompareFileNames(f1.Name, f2.Name);
617}
618*/
619
620struct CFolderRepack
621{
622  unsigned FolderIndex;
623  CNum NumCopyFiles;
624};
625
626/*
627static int CompareFolderRepacks(const CFolderRepack *p1, const CFolderRepack *p2, void *)
628{
629  int i1 = p1->FolderIndex;
630  int i2 = p2->FolderIndex;
631  // In that version we don't want to parse folders here, so we don't compare folders
632  // probably it must be improved in future
633  // const CDbEx &db = *(const CDbEx *)param;
634  // RINOZ(CompareFolders(
635  //     db.Folders[i1],
636  //     db.Folders[i2]));
637
638  return MyCompare(i1, i2);
639
640  // RINOZ_COMP(
641  //     db.NumUnpackStreamsVector[i1],
642  //     db.NumUnpackStreamsVector[i2]);
643  // if (db.NumUnpackStreamsVector[i1] == 0)
644  //   return 0;
645  // return CompareFiles(
646  //     db.Files[db.FolderStartFileIndex[i1]],
647  //     db.Files[db.FolderStartFileIndex[i2]]);
648}
649*/
650
651/*
652  we sort empty files and dirs in such order:
653  - Dir.NonAnti   (name sorted)
654  - File.NonAnti  (name sorted)
655  - File.Anti     (name sorted)
656  - Dir.Anti (reverse name sorted)
657*/
658
659static int CompareEmptyItems(const unsigned *p1, const unsigned *p2, void *param)
660{
661  const CObjectVector<CUpdateItem> &updateItems = *(const CObjectVector<CUpdateItem> *)param;
662  const CUpdateItem &u1 = updateItems[*p1];
663  const CUpdateItem &u2 = updateItems[*p2];
664  // NonAnti < Anti
665  if (u1.IsAnti != u2.IsAnti)
666    return (u1.IsAnti ? 1 : -1);
667  if (u1.IsDir != u2.IsDir)
668  {
669    // Dir.NonAnti < File < Dir.Anti
670    if (u1.IsDir)
671      return (u1.IsAnti ? 1 : -1);
672    return (u2.IsAnti ? -1 : 1);
673  }
674  int n = CompareFileNames(u1.Name, u2.Name);
675  return (u1.IsDir && u1.IsAnti) ? -n : n;
676}
677
678static const char *g_Exts =
679  " 7z xz lzma ace arc arj bz tbz bz2 tbz2 cab deb gz tgz ha lha lzh lzo lzx pak rar rpm sit zoo"
680  " zip jar ear war msi"
681  " 3gp avi mov mpeg mpg mpe wmv"
682  " aac ape fla flac la mp3 m4a mp4 ofr ogg pac ra rm rka shn swa tta wv wma wav"
683  " swf"
684  " chm hxi hxs"
685  " gif jpeg jpg jp2 png tiff  bmp ico psd psp"
686  " awg ps eps cgm dxf svg vrml wmf emf ai md"
687  " cad dwg pps key sxi"
688  " max 3ds"
689  " iso bin nrg mdf img pdi tar cpio xpi"
690  " vfd vhd vud vmc vsv"
691  " vmdk dsk nvram vmem vmsd vmsn vmss vmtm"
692  " inl inc idl acf asa"
693  " h hpp hxx c cpp cxx m mm go swift"
694  " rc java cs rs pas bas vb cls ctl frm dlg def"
695  " f77 f f90 f95"
696  " asm s"
697  " sql manifest dep"
698  " mak clw csproj vcproj sln dsp dsw"
699  " class"
700  " bat cmd bash sh"
701  " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml"
702  " awk sed hta js json php php3 php4 php5 phptml pl pm py pyo rb tcl ts vbs"
703  " text txt tex ans asc srt reg ini doc docx mcw dot rtf hlp xls xlr xlt xlw ppt pdf"
704  " sxc sxd sxi sxg sxw stc sti stw stm odt ott odg otg odp otp ods ots odf"
705  " abw afp cwk lwp wpd wps wpt wrf wri"
706  " abf afm bdf fon mgf otf pcf pfa snf ttf"
707  " dbf mdb nsf ntf wdb db fdb gdb"
708  " exe dll ocx vbx sfx sys tlb awx com obj lib out o so"
709  " pdb pch idb ncb opt";
710
711static unsigned GetExtIndex(const char *ext)
712{
713  unsigned extIndex = 1;
714  const char *p = g_Exts;
715  for (;;)
716  {
717    char c = *p++;
718    if (c == 0)
719      return extIndex;
720    if (c == ' ')
721      continue;
722    unsigned pos = 0;
723    for (;;)
724    {
725      char c2 = ext[pos++];
726      if (c2 == 0 && (c == 0 || c == ' '))
727        return extIndex;
728      if (c != c2)
729        break;
730      c = *p++;
731    }
732    extIndex++;
733    for (;;)
734    {
735      if (c == 0)
736        return extIndex;
737      if (c == ' ')
738        break;
739      c = *p++;
740    }
741  }
742}
743
744struct CRefItem
745{
746  const CUpdateItem *UpdateItem;
747  UInt32 Index;
748  unsigned ExtensionPos;
749  unsigned NamePos;
750  unsigned ExtensionIndex;
751
752  CRefItem() {}
753  CRefItem(UInt32 index, const CUpdateItem &ui, bool sortByType):
754    UpdateItem(&ui),
755    Index(index),
756    ExtensionPos(0),
757    NamePos(0),
758    ExtensionIndex(0)
759  {
760    if (sortByType)
761    {
762      int slashPos = ui.Name.ReverseFind_PathSepar();
763      NamePos = (unsigned)(slashPos + 1);
764      int dotPos = ui.Name.ReverseFind_Dot();
765      if (dotPos <= slashPos)
766        ExtensionPos = ui.Name.Len();
767      else
768      {
769        ExtensionPos = (unsigned)(dotPos + 1);
770        if (ExtensionPos != ui.Name.Len())
771        {
772          AString s;
773          for (unsigned pos = ExtensionPos;; pos++)
774          {
775            wchar_t c = ui.Name[pos];
776            if (c >= 0x80)
777              break;
778            if (c == 0)
779            {
780              ExtensionIndex = GetExtIndex(s);
781              break;
782            }
783            s += (char)MyCharLower_Ascii((char)c);
784          }
785        }
786      }
787    }
788  }
789};
790
791struct CSortParam
792{
793  // const CObjectVector<CTreeFolder> *TreeFolders;
794  bool SortByType;
795};
796
797/*
798  we sort files in such order:
799  - Dir.NonAnti   (name sorted)
800  - alt streams
801  - Dirs
802  - Dir.Anti (reverse name sorted)
803*/
804
805
806static int CompareUpdateItems(const CRefItem *p1, const CRefItem *p2, void *param)
807{
808  const CRefItem &a1 = *p1;
809  const CRefItem &a2 = *p2;
810  const CUpdateItem &u1 = *a1.UpdateItem;
811  const CUpdateItem &u2 = *a2.UpdateItem;
812
813  /*
814  if (u1.IsAltStream != u2.IsAltStream)
815    return u1.IsAltStream ? 1 : -1;
816  */
817
818  // Actually there are no dirs that time. They were stored in other steps
819  // So that code is unused?
820  if (u1.IsDir != u2.IsDir)
821    return u1.IsDir ? 1 : -1;
822  if (u1.IsDir)
823  {
824    if (u1.IsAnti != u2.IsAnti)
825      return (u1.IsAnti ? 1 : -1);
826    int n = CompareFileNames(u1.Name, u2.Name);
827    return -n;
828  }
829
830  // bool sortByType = *(bool *)param;
831  const CSortParam *sortParam = (const CSortParam *)param;
832  const bool sortByType = sortParam->SortByType;
833  if (sortByType)
834  {
835    RINOZ_COMP(a1.ExtensionIndex, a2.ExtensionIndex)
836    RINOZ(CompareFileNames(u1.Name.Ptr(a1.ExtensionPos), u2.Name.Ptr(a2.ExtensionPos)))
837    RINOZ(CompareFileNames(u1.Name.Ptr(a1.NamePos), u2.Name.Ptr(a2.NamePos)))
838    if (!u1.MTimeDefined && u2.MTimeDefined) return 1;
839    if (u1.MTimeDefined && !u2.MTimeDefined) return -1;
840    if (u1.MTimeDefined && u2.MTimeDefined) RINOZ_COMP(u1.MTime, u2.MTime)
841    RINOZ_COMP(u1.Size, u2.Size)
842  }
843  /*
844  int par1 = a1.UpdateItem->ParentFolderIndex;
845  int par2 = a2.UpdateItem->ParentFolderIndex;
846  const CTreeFolder &tf1 = (*sortParam->TreeFolders)[par1];
847  const CTreeFolder &tf2 = (*sortParam->TreeFolders)[par2];
848
849  int b1 = tf1.SortIndex, e1 = tf1.SortIndexEnd;
850  int b2 = tf2.SortIndex, e2 = tf2.SortIndexEnd;
851  if (b1 < b2)
852  {
853    if (e1 <= b2)
854      return -1;
855    // p2 in p1
856    int par = par2;
857    for (;;)
858    {
859      const CTreeFolder &tf = (*sortParam->TreeFolders)[par];
860      par = tf.Parent;
861      if (par == par1)
862      {
863        RINOZ(CompareFileNames(u1.Name, tf.Name));
864        break;
865      }
866    }
867  }
868  else if (b2 < b1)
869  {
870    if (e2 <= b1)
871      return 1;
872    // p1 in p2
873    int par = par1;
874    for (;;)
875    {
876      const CTreeFolder &tf = (*sortParam->TreeFolders)[par];
877      par = tf.Parent;
878      if (par == par2)
879      {
880        RINOZ(CompareFileNames(tf.Name, u2.Name));
881        break;
882      }
883    }
884  }
885  */
886  // RINOZ_COMP(a1.UpdateItem->ParentSortIndex, a2.UpdateItem->ParentSortIndex);
887  RINOK(CompareFileNames(u1.Name, u2.Name))
888  RINOZ_COMP(a1.UpdateItem->IndexInClient, a2.UpdateItem->IndexInClient)
889  RINOZ_COMP(a1.UpdateItem->IndexInArchive, a2.UpdateItem->IndexInArchive)
890  return 0;
891}
892
893struct CSolidGroup
894{
895  CRecordVector<UInt32> Indices;
896
897  CRecordVector<CFolderRepack> folderRefs;
898};
899
900static const char * const g_Exe_Exts[] =
901{
902    "dll"
903  , "exe"
904  , "ocx"
905  , "sfx"
906  , "sys"
907};
908
909static const char * const g_ExeUnix_Exts[] =
910{
911    "so"
912  , "dylib"
913};
914
915static bool IsExt_Exe(const wchar_t *ext)
916{
917  for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_Exe_Exts); i++)
918    if (StringsAreEqualNoCase_Ascii(ext, g_Exe_Exts[i]))
919      return true;
920  return false;
921}
922
923/*
924static bool IsExt_ExeUnix(const wchar_t *ext)
925{
926  for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_ExeUnix_Exts); i++)
927    if (StringsAreEqualNoCase_Ascii(ext, g_ExeUnix_Exts[i]))
928      return true;
929  return false;
930}
931*/
932
933// we try to find "so" extension in such name: libstdc++.so.6.0.29
934static bool IsExt_ExeUnix_NumericAllowed(const UString &path)
935{
936  unsigned pos = path.Len();
937  unsigned dotPos = pos;
938  for (;;)
939  {
940    if (pos == 0)
941      return false;
942    const wchar_t c = path[--pos];
943    if (IS_PATH_SEPAR(c))
944      return false;
945    if (c == '.')
946    {
947      const unsigned num = (dotPos - pos) - 1;
948      if (num < 1)
949        return false;
950      const wchar_t *cur = path.Ptr(pos + 1);
951      for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_ExeUnix_Exts); i++)
952      {
953        const char *ext = g_ExeUnix_Exts[i];
954        if (num == MyStringLen(ext))
955          if (IsString1PrefixedByString2_NoCase_Ascii(cur, ext))
956            return true;
957      }
958      const wchar_t *end;
959      ConvertStringToUInt32(cur, &end);
960      if ((size_t)(end - cur) != num)
961        return false;
962      dotPos = pos;
963    }
964  }
965}
966
967
968struct CAnalysis
969{
970  CMyComPtr<IArchiveUpdateCallbackFile> Callback;
971  CByteBuffer Buffer;
972
973  bool ParseWav;
974  bool ParseExe;
975  bool ParseExeUnix;
976  bool ParseNoExt;
977  bool ParseAll;
978
979  /*
980  bool Need_ATime;
981  bool ATime_Defined;
982  FILETIME ATime;
983  */
984
985  CAnalysis():
986      ParseWav(false),
987      ParseExe(false),
988      ParseExeUnix(false),
989      ParseNoExt(false),
990      ParseAll(false)
991      /*
992      , Need_ATime(false)
993      , ATime_Defined(false)
994      */
995  {}
996
997  HRESULT GetFilterGroup(UInt32 index, const CUpdateItem &ui, CFilterMode &filterMode);
998};
999
1000static const size_t kAnalysisBufSize = 1 << 14;
1001
1002HRESULT CAnalysis::GetFilterGroup(UInt32 index, const CUpdateItem &ui, CFilterMode &filterMode)
1003{
1004  filterMode.Id = 0;
1005  filterMode.Delta = 0;
1006  filterMode.Offset = 0;
1007
1008  CFilterMode filterModeTemp = filterMode;
1009
1010  const int slashPos = ui.Name.ReverseFind_PathSepar();
1011  const int dotPos = ui.Name.ReverseFind_Dot();
1012
1013  // if (dotPos > slashPos)
1014  {
1015    bool needReadFile = ParseAll;
1016    /* if (Callback) is not supported by client,
1017       we still try to use file name extension to detect executable file */
1018    bool probablyIsSameIsa = false;
1019
1020    if (!needReadFile || !Callback)
1021    {
1022      const wchar_t *ext = NULL;
1023      if (dotPos > slashPos)
1024        ext = ui.Name.Ptr((unsigned)(dotPos + 1));
1025      // 7-zip stores posix attributes in high 16 bits and sets (0x8000) flag
1026      if (ui.Attrib & 0x8000)
1027      {
1028        const unsigned st_mode = ui.Attrib >> 16;
1029        /* note: executable ".so" can be without execute permission,
1030           and symbolic link to such ".so" file is possible */
1031        // st_mode = 00111; // for debug
1032        /* in Linux we expect such permissions:
1033             0755 : for most executables
1034             0644 : for some ".so" files
1035             0777 : in WSL for all files.
1036                    We can try to exclude some such 0777 cases from analysis,
1037                    if there is non-executable extension.
1038        */
1039
1040        if ((st_mode & (
1041              MY_LIN_S_IXUSR |
1042              MY_LIN_S_IXGRP |
1043              MY_LIN_S_IXOTH)) != 0
1044            && MY_LIN_S_ISREG(st_mode)
1045            && (ui.Size >= (1u << 11)))
1046        {
1047          #ifndef _WIN32
1048          probablyIsSameIsa = true;
1049          #endif
1050          needReadFile = true;
1051        }
1052      }
1053
1054      if (!needReadFile)
1055      {
1056        if (!ext)
1057          needReadFile = ParseNoExt;
1058        else
1059        {
1060          bool isUnixExt = false;
1061          if (ParseExeUnix)
1062            isUnixExt = IsExt_ExeUnix_NumericAllowed(ui.Name);
1063          if (isUnixExt)
1064          {
1065            needReadFile = true;
1066            #ifndef _WIN32
1067              probablyIsSameIsa = true;
1068            #endif
1069          }
1070          else if (IsExt_Exe(ext))
1071          {
1072            needReadFile = ParseExe;
1073            #ifdef _WIN32
1074              probablyIsSameIsa = true;
1075            #endif
1076          }
1077          else if (StringsAreEqualNoCase_Ascii(ext, "wav"))
1078          {
1079            if (!needReadFile)
1080              needReadFile = ParseWav;
1081          }
1082        }
1083      }
1084    }
1085
1086    if (needReadFile)
1087    {
1088      BoolInt parseRes = false;
1089      if (Callback)
1090      {
1091        if (Buffer.Size() != kAnalysisBufSize)
1092          Buffer.Alloc(kAnalysisBufSize);
1093        CMyComPtr<ISequentialInStream> stream;
1094        HRESULT result = Callback->GetStream2(index, &stream, NUpdateNotifyOp::kAnalyze);
1095        if (result == S_OK && stream)
1096        {
1097          /*
1098          if (Need_ATime)
1099          {
1100            // access time could be changed in analysis pass
1101            CMyComPtr<IStreamGetProps> getProps;
1102            stream.QueryInterface(IID_IStreamGetProps, (void **)&getProps);
1103            if (getProps)
1104              if (getProps->GetProps(NULL, NULL, &ATime, NULL, NULL) == S_OK)
1105                ATime_Defined = true;
1106          }
1107          */
1108          size_t size = kAnalysisBufSize;
1109          result = ReadStream(stream, Buffer, &size);
1110          stream.Release();
1111          // RINOK(Callback->SetOperationResult2(index, NUpdate::NOperationResult::kOK));
1112          if (result == S_OK)
1113          {
1114            parseRes = ParseFile(Buffer, size, &filterModeTemp);
1115          }
1116        }
1117      } // Callback
1118      else if (probablyIsSameIsa)
1119      {
1120        #ifdef MY_CPU_X86_OR_AMD64
1121          filterModeTemp.Id = k_X86;
1122        #endif
1123        #ifdef MY_CPU_ARM64
1124          filterModeTemp.Id = k_ARM64;
1125        #endif
1126        parseRes = true;
1127      }
1128
1129      if (parseRes
1130          && filterModeTemp.Id != k_Delta
1131          && filterModeTemp.Delta == 0)
1132      {
1133        /* ParseFile() sets (filterModeTemp.Delta == 0) for all
1134           methods except of k_Delta. */
1135        // it's not k_Delta
1136        // So we call SetDelta() to set Delta
1137        filterModeTemp.SetDelta();
1138        if (filterModeTemp.Delta > 1)
1139        {
1140          /* If file Size is not aligned, then branch filter
1141             will not work for next file in solid block.
1142             Maybe we should allow filter for non-aligned-size file in non-solid archives ?
1143          */
1144          if (ui.Size % filterModeTemp.Delta != 0)
1145            parseRes = false;
1146          // windows exe files are not aligned for 4 KiB.
1147          /*
1148          else if (filterModeTemp.Id == k_ARM64 && filterModeTemp.Offset != 0)
1149          {
1150            if (ui.Size % (1 << 12) != 0)
1151            {
1152              // If Size is not aligned for 4 KiB, then Offset will not work for next file in solid block.
1153              // so we place such file in group with (Offset==0).
1154              filterModeTemp.Offset = 0;
1155            }
1156          }
1157          */
1158        }
1159      }
1160      if (!parseRes)
1161        filterModeTemp.ClearFilterMode();
1162    }
1163  }
1164
1165  filterMode = filterModeTemp;
1166  return S_OK;
1167}
1168
1169static inline void GetMethodFull(UInt64 methodID, UInt32 numStreams, CMethodFull &m)
1170{
1171  m.Id = methodID;
1172  m.NumStreams = numStreams;
1173}
1174
1175static HRESULT AddBondForFilter(CCompressionMethodMode &mode)
1176{
1177  for (unsigned c = 1; c < mode.Methods.Size(); c++)
1178  {
1179    if (!mode.IsThereBond_to_Coder(c))
1180    {
1181      CBond2 bond;
1182      bond.OutCoder = 0;
1183      bond.OutStream = 0;
1184      bond.InCoder = c;
1185      mode.Bonds.Add(bond);
1186      return S_OK;
1187    }
1188  }
1189  return E_INVALIDARG;
1190}
1191
1192static HRESULT AddFilterBond(CCompressionMethodMode &mode)
1193{
1194  if (!mode.Bonds.IsEmpty())
1195    return AddBondForFilter(mode);
1196  return S_OK;
1197}
1198
1199static HRESULT AddBcj2Methods(CCompressionMethodMode &mode)
1200{
1201  // mode.Methods[0] must be k_BCJ2 method !
1202
1203  CMethodFull m;
1204  GetMethodFull(k_LZMA, 1, m);
1205
1206  m.AddProp32(NCoderPropID::kDictionarySize, 1 << 20);
1207  m.AddProp32(NCoderPropID::kNumFastBytes, 128);
1208  m.AddProp32(NCoderPropID::kNumThreads, 1);
1209  m.AddProp32(NCoderPropID::kLitPosBits, 2);
1210  m.AddProp32(NCoderPropID::kLitContextBits, 0);
1211  // m.AddProp_Ascii(NCoderPropID::kMatchFinder, "BT2");
1212
1213  unsigned methodIndex = mode.Methods.Size();
1214
1215  if (mode.Bonds.IsEmpty())
1216  {
1217    for (unsigned i = 1; i + 1 < mode.Methods.Size(); i++)
1218    {
1219      CBond2 bond;
1220      bond.OutCoder = i;
1221      bond.OutStream = 0;
1222      bond.InCoder = i + 1;
1223      mode.Bonds.Add(bond);
1224    }
1225  }
1226
1227  mode.Methods.Add(m);
1228  mode.Methods.Add(m);
1229
1230  RINOK(AddBondForFilter(mode))
1231  CBond2 bond;
1232  bond.OutCoder = 0;
1233  bond.InCoder = methodIndex;      bond.OutStream = 1;  mode.Bonds.Add(bond);
1234  bond.InCoder = methodIndex + 1;  bond.OutStream = 2;  mode.Bonds.Add(bond);
1235  return S_OK;
1236}
1237
1238static HRESULT MakeExeMethod(CCompressionMethodMode &mode,
1239    const CFilterMode &filterMode, /* bool addFilter, */ bool bcj2Filter)
1240{
1241  if (mode.Filter_was_Inserted)
1242  {
1243    const CMethodFull &m = mode.Methods[0];
1244    const CMethodId id = m.Id;
1245    if (id == k_BCJ2)
1246      return AddBcj2Methods(mode);
1247    if (!m.IsSimpleCoder())
1248      return E_NOTIMPL;
1249    // if (Bonds.IsEmpty()) we can create bonds later
1250    return AddFilterBond(mode);
1251  }
1252
1253  if (filterMode.Id == 0)
1254    return S_OK;
1255
1256  CMethodFull &m = mode.Methods.InsertNew(0);
1257
1258  {
1259    FOR_VECTOR (k, mode.Bonds)
1260    {
1261      CBond2 &bond = mode.Bonds[k];
1262      bond.InCoder++;
1263      bond.OutCoder++;
1264    }
1265  }
1266
1267  HRESULT res;
1268
1269  if (bcj2Filter && Is86Filter(filterMode.Id))
1270  {
1271    GetMethodFull(k_BCJ2, 4, m);
1272    res = AddBcj2Methods(mode);
1273  }
1274  else
1275  {
1276    GetMethodFull(filterMode.Id, 1, m);
1277    if (filterMode.Id == k_Delta)
1278      m.AddProp32(NCoderPropID::kDefaultProp, filterMode.Delta);
1279    else if (filterMode.Id == k_ARM64)
1280    {
1281      // if (filterMode.Offset != 0)
1282        m.AddProp32(
1283          NCoderPropID::kDefaultProp,
1284          // NCoderPropID::kBranchOffset,
1285          filterMode.Offset);
1286    }
1287    res = AddFilterBond(mode);
1288
1289    int alignBits = -1;
1290    {
1291      const UInt32 delta = filterMode.Delta;
1292      if (delta == 0 || delta > 16)
1293      {
1294        // if (delta == 0) alignBits = GetAlignForFilterMethod(filterMode.Id);
1295      }
1296      else if ((delta & ((1 << 4) - 1)) == 0) alignBits = 4;
1297      else if ((delta & ((1 << 3) - 1)) == 0) alignBits = 3;
1298      else if ((delta & ((1 << 2) - 1)) == 0) alignBits = 2;
1299      else if ((delta & ((1 << 1) - 1)) == 0) alignBits = 1;
1300      // else alignBits = 0;
1301      /* alignBits=0 is default mode for lzma/lzma2.
1302         So we don't set alignBits=0 here. */
1303    }
1304    if (res == S_OK && alignBits > 0)
1305    {
1306      unsigned nextCoder = 1;
1307      if (!mode.Bonds.IsEmpty())
1308      {
1309        nextCoder = mode.Bonds.Back().InCoder;
1310      }
1311      if (nextCoder < mode.Methods.Size())
1312      {
1313        CMethodFull &nextMethod = mode.Methods[nextCoder];
1314        if (nextMethod.Id == k_LZMA || nextMethod.Id == k_LZMA2)
1315        {
1316          if (!nextMethod.Are_Lzma_Model_Props_Defined())
1317          {
1318            if (alignBits != 0)
1319            {
1320              if (alignBits > 2 || filterMode.Id == k_Delta)
1321                nextMethod.AddProp32(NCoderPropID::kPosStateBits, (unsigned)alignBits);
1322              unsigned lc = 0;
1323              if (alignBits < 3)
1324                lc = (unsigned)(3 - alignBits);
1325              nextMethod.AddProp32(NCoderPropID::kLitContextBits, lc);
1326              nextMethod.AddProp32(NCoderPropID::kLitPosBits, (unsigned)alignBits);
1327            }
1328          }
1329        }
1330      }
1331    }
1332  }
1333
1334  return res;
1335}
1336
1337
1338static void UpdateItem_To_FileItem2(const CUpdateItem &ui, CFileItem2 &file2)
1339{
1340  file2.Attrib = ui.Attrib;  file2.AttribDefined = ui.AttribDefined;
1341  file2.CTime = ui.CTime;  file2.CTimeDefined = ui.CTimeDefined;
1342  file2.ATime = ui.ATime;  file2.ATimeDefined = ui.ATimeDefined;
1343  file2.MTime = ui.MTime;  file2.MTimeDefined = ui.MTimeDefined;
1344  file2.IsAnti = ui.IsAnti;
1345  // file2.IsAux = false;
1346  file2.StartPosDefined = false;
1347  // file2.StartPos = 0;
1348}
1349
1350
1351static void UpdateItem_To_FileItem(const CUpdateItem &ui,
1352    CFileItem &file, CFileItem2 &file2)
1353{
1354  UpdateItem_To_FileItem2(ui, file2);
1355
1356  file.Size = ui.Size;
1357  file.IsDir = ui.IsDir;
1358  file.HasStream = ui.HasStream();
1359  // file.IsAltStream = ui.IsAltStream;
1360}
1361
1362
1363
1364Z7_CLASS_IMP_COM_2(
1365  CRepackInStreamWithSizes
1366  , ISequentialInStream
1367  , ICompressGetSubStreamSize
1368)
1369  CMyComPtr<ISequentialInStream> _stream;
1370  UInt64 _size;
1371  const CBoolVector *_extractStatuses;
1372  UInt32 _startIndex;
1373public:
1374  const CDbEx *_db;
1375
1376  void Init(ISequentialInStream *stream, UInt32 startIndex, const CBoolVector *extractStatuses)
1377  {
1378    _startIndex = startIndex;
1379    _extractStatuses = extractStatuses;
1380    _size = 0;
1381    _stream = stream;
1382  }
1383  UInt64 GetSize() const { return _size; }
1384};
1385
1386Z7_COM7F_IMF(CRepackInStreamWithSizes::Read(void *data, UInt32 size, UInt32 *processedSize))
1387{
1388  UInt32 realProcessedSize;
1389  const HRESULT result = _stream->Read(data, size, &realProcessedSize);
1390  _size += realProcessedSize;
1391  if (processedSize)
1392    *processedSize = realProcessedSize;
1393  return result;
1394}
1395
1396Z7_COM7F_IMF(CRepackInStreamWithSizes::GetSubStreamSize(UInt64 subStream, UInt64 *value))
1397{
1398  *value = 0;
1399  if (subStream >= _extractStatuses->Size())
1400    return S_FALSE; // E_FAIL;
1401  const unsigned index = (unsigned)subStream;
1402  if ((*_extractStatuses)[index])
1403  {
1404    const CFileItem &fi = _db->Files[_startIndex + index];
1405    if (fi.HasStream)
1406      *value = fi.Size;
1407  }
1408  return S_OK;
1409}
1410
1411
1412class CRepackStreamBase
1413{
1414protected:
1415  bool _needWrite;
1416  bool _fileIsOpen;
1417  bool _calcCrc;
1418  UInt32 _crc;
1419  UInt64 _rem;
1420
1421  const CBoolVector *_extractStatuses;
1422  UInt32 _startIndex;
1423  unsigned _currentIndex;
1424
1425  HRESULT OpenFile();
1426  HRESULT CloseFile();
1427  HRESULT ProcessEmptyFiles();
1428
1429public:
1430  const CDbEx *_db;
1431  CMyComPtr<IArchiveUpdateCallbackFile> _opCallback;
1432  CMyComPtr<IArchiveExtractCallbackMessage2> _extractCallback;
1433
1434  HRESULT Init(UInt32 startIndex, const CBoolVector *extractStatuses);
1435  HRESULT CheckFinishedState() const { return (_currentIndex == _extractStatuses->Size()) ? S_OK: E_FAIL; }
1436};
1437
1438HRESULT CRepackStreamBase::Init(UInt32 startIndex, const CBoolVector *extractStatuses)
1439{
1440  _startIndex = startIndex;
1441  _extractStatuses = extractStatuses;
1442
1443  _currentIndex = 0;
1444  _fileIsOpen = false;
1445
1446  return ProcessEmptyFiles();
1447}
1448
1449HRESULT CRepackStreamBase::OpenFile()
1450{
1451  UInt32 arcIndex = _startIndex + _currentIndex;
1452  const CFileItem &fi = _db->Files[arcIndex];
1453
1454  _needWrite = (*_extractStatuses)[_currentIndex];
1455  if (_opCallback)
1456  {
1457    RINOK(_opCallback->ReportOperation(
1458        NEventIndexType::kInArcIndex, arcIndex,
1459        _needWrite ?
1460            NUpdateNotifyOp::kRepack :
1461            NUpdateNotifyOp::kSkip))
1462  }
1463
1464  _crc = CRC_INIT_VAL;
1465  _calcCrc = (fi.CrcDefined && !fi.IsDir);
1466
1467  _fileIsOpen = true;
1468  _rem = fi.Size;
1469  return S_OK;
1470}
1471
1472const HRESULT k_My_HRESULT_CRC_ERROR = 0x20000002;
1473
1474HRESULT CRepackStreamBase::CloseFile()
1475{
1476  UInt32 arcIndex = _startIndex + _currentIndex;
1477  const CFileItem &fi = _db->Files[arcIndex];
1478  _fileIsOpen = false;
1479  _currentIndex++;
1480  if (!_calcCrc || fi.Crc == CRC_GET_DIGEST(_crc))
1481    return S_OK;
1482
1483  if (_extractCallback)
1484  {
1485    RINOK(_extractCallback->ReportExtractResult(
1486        NEventIndexType::kInArcIndex, arcIndex,
1487        NExtract::NOperationResult::kCRCError))
1488  }
1489  // return S_FALSE;
1490  return k_My_HRESULT_CRC_ERROR;
1491}
1492
1493HRESULT CRepackStreamBase::ProcessEmptyFiles()
1494{
1495  while (_currentIndex < _extractStatuses->Size() && _db->Files[_startIndex + _currentIndex].Size == 0)
1496  {
1497    RINOK(OpenFile())
1498    RINOK(CloseFile())
1499  }
1500  return S_OK;
1501}
1502
1503
1504
1505#ifndef Z7_ST
1506
1507class CFolderOutStream2 Z7_final:
1508  public CRepackStreamBase,
1509  public ISequentialOutStream,
1510  public CMyUnknownImp
1511{
1512  Z7_COM_UNKNOWN_IMP_0
1513  Z7_IFACE_COM7_IMP(ISequentialOutStream)
1514public:
1515  CMyComPtr<ISequentialOutStream> _stream;
1516};
1517
1518Z7_COM7F_IMF(CFolderOutStream2::Write(const void *data, UInt32 size, UInt32 *processedSize))
1519{
1520  if (processedSize)
1521    *processedSize = 0;
1522
1523  while (size != 0)
1524  {
1525    if (_fileIsOpen)
1526    {
1527      UInt32 cur = (size < _rem ? size : (UInt32)_rem);
1528      HRESULT result = S_OK;
1529      if (_needWrite)
1530        result = _stream->Write(data, cur, &cur);
1531      if (_calcCrc)
1532        _crc = CrcUpdate(_crc, data, cur);
1533      if (processedSize)
1534        *processedSize += cur;
1535      data = (const Byte *)data + cur;
1536      size -= cur;
1537      _rem -= cur;
1538      if (_rem == 0)
1539      {
1540        RINOK(CloseFile())
1541        RINOK(ProcessEmptyFiles())
1542      }
1543      RINOK(result)
1544      if (cur == 0)
1545        break;
1546      continue;
1547    }
1548
1549    RINOK(ProcessEmptyFiles())
1550    if (_currentIndex == _extractStatuses->Size())
1551    {
1552      // we don't support write cut here
1553      return E_FAIL;
1554    }
1555    RINOK(OpenFile())
1556  }
1557
1558  return S_OK;
1559}
1560
1561#endif
1562
1563
1564
1565static const UInt32 kTempBufSize = 1 << 16;
1566
1567class CFolderInStream2 Z7_final:
1568  public CRepackStreamBase,
1569  public ISequentialInStream,
1570  public CMyUnknownImp
1571{
1572  Z7_COM_UNKNOWN_IMP_0
1573  Z7_IFACE_COM7_IMP(ISequentialInStream)
1574
1575  Byte *_buf;
1576public:
1577  CMyComPtr<ISequentialInStream> _inStream;
1578  HRESULT Result;
1579
1580  CFolderInStream2():
1581      Result(S_OK)
1582  {
1583    _buf = new Byte[kTempBufSize];
1584  }
1585
1586  ~CFolderInStream2()
1587  {
1588    delete []_buf;
1589  }
1590
1591  void Init() { Result = S_OK; }
1592};
1593
1594Z7_COM7F_IMF(CFolderInStream2::Read(void *data, UInt32 size, UInt32 *processedSize))
1595{
1596  if (processedSize)
1597    *processedSize = 0;
1598
1599  while (size != 0)
1600  {
1601    if (_fileIsOpen)
1602    {
1603      UInt32 cur = (size < _rem ? size : (UInt32)_rem);
1604
1605      void *buf;
1606      if (_needWrite)
1607        buf = data;
1608      else
1609      {
1610        buf = _buf;
1611        if (cur > kTempBufSize)
1612          cur = kTempBufSize;
1613      }
1614
1615      const HRESULT result = _inStream->Read(buf, cur, &cur);
1616      _crc = CrcUpdate(_crc, buf, cur);
1617      _rem -= cur;
1618
1619      if (_needWrite)
1620      {
1621        data = (Byte *)data + cur;
1622        size -= cur;
1623        if (processedSize)
1624          *processedSize += cur;
1625      }
1626
1627      if (result != S_OK)
1628        Result = result;
1629
1630      if (_rem == 0)
1631      {
1632        RINOK(CloseFile())
1633        RINOK(ProcessEmptyFiles())
1634      }
1635
1636      RINOK(result)
1637
1638      if (cur == 0)
1639        return E_FAIL;
1640
1641      continue;
1642    }
1643
1644    RINOK(ProcessEmptyFiles())
1645    if (_currentIndex == _extractStatuses->Size())
1646    {
1647      return S_OK;
1648    }
1649    RINOK(OpenFile())
1650  }
1651
1652  return S_OK;
1653}
1654
1655
1656class CThreadDecoder Z7_final
1657  #ifndef Z7_ST
1658    : public CVirtThread
1659  #endif
1660{
1661public:
1662  CDecoder Decoder;
1663
1664  CThreadDecoder(bool multiThreadMixer):
1665      Decoder(multiThreadMixer)
1666  {
1667    #ifndef Z7_ST
1668    if (multiThreadMixer)
1669    {
1670      MtMode = false;
1671      NumThreads = 1;
1672      FosSpec = new CFolderOutStream2;
1673      Fos = FosSpec;
1674      Result = E_FAIL;
1675    }
1676    #endif
1677    // UnpackSize = 0;
1678    // send_UnpackSize = false;
1679  }
1680
1681  #ifndef Z7_ST
1682
1683  bool dataAfterEnd_Error;
1684  HRESULT Result;
1685  CMyComPtr<IInStream> InStream;
1686
1687  CFolderOutStream2 *FosSpec;
1688  CMyComPtr<ISequentialOutStream> Fos;
1689
1690  UInt64 StartPos;
1691  const CFolders *Folders;
1692  unsigned FolderIndex;
1693
1694  // bool send_UnpackSize;
1695  // UInt64 UnpackSize;
1696
1697  #ifndef Z7_NO_CRYPTO
1698  CMyComPtr<ICryptoGetTextPassword> getTextPassword;
1699  #endif
1700
1701  DECL_EXTERNAL_CODECS_LOC_VARS_DECL
1702
1703  #ifndef Z7_ST
1704  bool MtMode;
1705  UInt32 NumThreads;
1706  #endif
1707
1708
1709  ~CThreadDecoder() Z7_DESTRUCTOR_override
1710  {
1711    /* WaitThreadFinish() will be called in ~CVirtThread().
1712       But we need WaitThreadFinish() call before
1713       destructors of this class members.
1714    */
1715    CVirtThread::WaitThreadFinish();
1716  }
1717private:
1718  virtual void Execute() Z7_override;
1719
1720  #endif
1721};
1722
1723#ifndef Z7_ST
1724
1725void CThreadDecoder::Execute()
1726{
1727  try
1728  {
1729    #ifndef Z7_NO_CRYPTO
1730      bool isEncrypted = false;
1731      bool passwordIsDefined = false;
1732      UString password;
1733    #endif
1734
1735    dataAfterEnd_Error = false;
1736
1737    Result = Decoder.Decode(
1738      EXTERNAL_CODECS_LOC_VARS
1739      InStream,
1740      StartPos,
1741      *Folders, FolderIndex,
1742
1743      // send_UnpackSize ? &UnpackSize : NULL,
1744      NULL, // unpackSize : FULL unpack
1745
1746      Fos,
1747      NULL, // compressProgress
1748
1749      NULL  // *inStreamMainRes
1750      , dataAfterEnd_Error
1751
1752      Z7_7Z_DECODER_CRYPRO_VARS
1753      #ifndef Z7_ST
1754        , MtMode, NumThreads,
1755        0 // MemUsage
1756      #endif
1757
1758      );
1759  }
1760  catch(...)
1761  {
1762    Result = E_FAIL;
1763  }
1764
1765  /*
1766  if (Result == S_OK)
1767    Result = FosSpec->CheckFinishedState();
1768  */
1769  FosSpec->_stream.Release();
1770}
1771
1772#endif
1773
1774#ifndef Z7_NO_CRYPTO
1775
1776Z7_CLASS_IMP_NOQIB_1(
1777  CCryptoGetTextPassword
1778  , ICryptoGetTextPassword
1779)
1780public:
1781  UString Password;
1782};
1783
1784Z7_COM7F_IMF(CCryptoGetTextPassword::CryptoGetTextPassword(BSTR *password))
1785{
1786  return StringToBstr(Password, password);
1787}
1788
1789#endif
1790
1791
1792static void GetFile(const CDatabase &inDb, unsigned index, CFileItem &file, CFileItem2 &file2)
1793{
1794  file = inDb.Files[index];
1795  file2.CTimeDefined = inDb.CTime.GetItem(index, file2.CTime);
1796  file2.ATimeDefined = inDb.ATime.GetItem(index, file2.ATime);
1797  file2.MTimeDefined = inDb.MTime.GetItem(index, file2.MTime);
1798  file2.StartPosDefined = inDb.StartPos.GetItem(index, file2.StartPos);
1799  file2.AttribDefined = inDb.Attrib.GetItem(index, file2.Attrib);
1800  file2.IsAnti = inDb.IsItemAnti(index);
1801  // file2.IsAux = inDb.IsItemAux(index);
1802}
1803
1804HRESULT Update(
1805    DECL_EXTERNAL_CODECS_LOC_VARS
1806    IInStream *inStream,
1807    const CDbEx *db,
1808    CObjectVector<CUpdateItem> &updateItems,
1809    // const CObjectVector<CTreeFolder> &treeFolders,
1810    // const CUniqBlocks &secureBlocks,
1811    ISequentialOutStream *seqOutStream,
1812    IArchiveUpdateCallback *updateCallback,
1813    const CUpdateOptions &options)
1814{
1815  UInt64 numSolidFiles = options.NumSolidFiles;
1816  if (numSolidFiles == 0)
1817    numSolidFiles = 1;
1818
1819  Z7_DECL_CMyComPtr_QI_FROM(
1820      IArchiveUpdateCallbackFile,
1821      opCallback, updateCallback)
1822
1823  Z7_DECL_CMyComPtr_QI_FROM(
1824      IArchiveExtractCallbackMessage2,
1825      extractCallback, updateCallback)
1826
1827  /*
1828  Z7_DECL_CMyComPtr_QI_FROM(
1829      IArchiveUpdateCallbackArcProp,
1830      reportArcProp, updateCallback)
1831  */
1832
1833  // size_t totalSecureDataSize = (size_t)secureBlocks.GetTotalSizeInBytes();
1834
1835  CMyComPtr<IStreamSetRestriction> v_StreamSetRestriction;
1836  {
1837    Z7_DECL_CMyComPtr_QI_FROM(
1838        IOutStream,
1839        outStream, seqOutStream)
1840    if (!outStream)
1841      return E_NOTIMPL;
1842    const UInt64 sfxBlockSize = (db && !options.RemoveSfxBlock) ?
1843        db->ArcInfo.StartPosition: 0;
1844    seqOutStream->QueryInterface(IID_IStreamSetRestriction, (void **)&v_StreamSetRestriction);
1845    if (v_StreamSetRestriction)
1846    {
1847      UInt64 offset = 0;
1848      RINOK(outStream->Seek(0, STREAM_SEEK_CUR, &offset))
1849      RINOK(v_StreamSetRestriction->SetRestriction(
1850          outStream ? offset + sfxBlockSize : 0,
1851          outStream ? offset + sfxBlockSize + k_StartHeadersRewriteSize : 0))
1852    }
1853    outStream.Release();
1854    if (sfxBlockSize != 0)
1855    {
1856      RINOK(WriteRange(inStream, seqOutStream, 0, sfxBlockSize, NULL))
1857    }
1858  }
1859
1860  CIntArr fileIndexToUpdateIndexMap;
1861  UInt64 complexity = 0;
1862  UInt64 inSizeForReduce2 = 0;
1863
1864 #ifndef Z7_NO_CRYPTO
1865  bool needEncryptedRepack = false;
1866 #endif
1867
1868  CRecordVector<CFilterMode2> filters;
1869  CObjectVector<CSolidGroup> groups;
1870
1871  #ifndef Z7_ST
1872  bool thereAreRepacks = false;
1873  #endif
1874
1875  bool useFilters = options.UseFilters;
1876  if (useFilters)
1877  {
1878    const CCompressionMethodMode &method = *options.Method;
1879
1880    FOR_VECTOR (i, method.Methods)
1881    {
1882      /* IsFilterMethod() knows only built-in codecs
1883         FIXME: we should check IsFilter status for external filters too */
1884      if (IsFilterMethod(method.Methods[i].Id))
1885      {
1886        useFilters = false;
1887        break;
1888      }
1889    }
1890  }
1891
1892  if (db)
1893  {
1894    fileIndexToUpdateIndexMap.Alloc(db->Files.Size());
1895    unsigned i;
1896
1897    for (i = 0; i < db->Files.Size(); i++)
1898      fileIndexToUpdateIndexMap[i] = -1;
1899
1900    for (i = 0; i < updateItems.Size(); i++)
1901    {
1902      int index = updateItems[i].IndexInArchive;
1903      if (index != -1)
1904        fileIndexToUpdateIndexMap[(unsigned)index] = (int)i;
1905    }
1906
1907    for (i = 0; i < db->NumFolders; i++)
1908    {
1909      CNum indexInFolder = 0;
1910      CNum numCopyItems = 0;
1911      const CNum numUnpackStreams = db->NumUnpackStreamsVector[i];
1912      UInt64 repackSize = 0;
1913
1914      for (CNum fi = db->FolderStartFileIndex[i]; indexInFolder < numUnpackStreams; fi++)
1915      {
1916        if (fi >= db->Files.Size())
1917          return E_FAIL;
1918
1919        const CFileItem &file = db->Files[fi];
1920        if (file.HasStream)
1921        {
1922          indexInFolder++;
1923          const int updateIndex = fileIndexToUpdateIndexMap[fi];
1924          if (updateIndex >= 0 && !updateItems[(unsigned)updateIndex].NewData)
1925          {
1926            numCopyItems++;
1927            repackSize += file.Size;
1928          }
1929        }
1930      }
1931
1932      if (numCopyItems == 0)
1933        continue;
1934
1935      CFolderRepack rep;
1936      rep.FolderIndex = i;
1937      rep.NumCopyFiles = numCopyItems;
1938      CFolderEx f;
1939      db->ParseFolderEx(i, f);
1940
1941     #ifndef Z7_NO_CRYPTO
1942      const bool isEncrypted = f.IsEncrypted();
1943     #endif
1944      const bool needCopy = (numCopyItems == numUnpackStreams);
1945      const bool extractFilter = (useFilters || needCopy);
1946
1947      const unsigned groupIndex = Get_FilterGroup_for_Folder(filters, f, extractFilter);
1948
1949      while (groupIndex >= groups.Size())
1950        groups.AddNew();
1951
1952      groups[groupIndex].folderRefs.Add(rep);
1953
1954      if (needCopy)
1955        complexity += db->GetFolderFullPackSize(i);
1956      else
1957      {
1958        #ifndef Z7_ST
1959        thereAreRepacks = true;
1960        #endif
1961        complexity += repackSize;
1962        if (inSizeForReduce2 < repackSize)
1963          inSizeForReduce2 = repackSize;
1964       #ifndef Z7_NO_CRYPTO
1965        if (isEncrypted)
1966          needEncryptedRepack = true;
1967       #endif
1968      }
1969    }
1970  }
1971
1972  UInt64 inSizeForReduce = 0;
1973  {
1974    bool isSolid = (numSolidFiles > 1 && options.NumSolidBytes != 0);
1975    FOR_VECTOR (i, updateItems)
1976    {
1977      const CUpdateItem &ui = updateItems[i];
1978      if (ui.NewData)
1979      {
1980        complexity += ui.Size;
1981        if (isSolid)
1982          inSizeForReduce += ui.Size;
1983        else if (inSizeForReduce < ui.Size)
1984          inSizeForReduce = ui.Size;
1985      }
1986    }
1987  }
1988
1989  if (inSizeForReduce < inSizeForReduce2)
1990    inSizeForReduce = inSizeForReduce2;
1991
1992  RINOK(updateCallback->SetTotal(complexity))
1993
1994  CLocalProgress *lps = new CLocalProgress;
1995  CMyComPtr<ICompressProgressInfo> progress = lps;
1996  lps->Init(updateCallback, true);
1997
1998  #ifndef Z7_ST
1999
2000  CStreamBinder sb;
2001  /*
2002  if (options.MultiThreadMixer)
2003  {
2004    RINOK(sb.CreateEvents());
2005  }
2006  */
2007
2008  #endif
2009
2010  CThreadDecoder threadDecoder(options.MultiThreadMixer);
2011
2012  #ifndef Z7_ST
2013  if (options.MultiThreadMixer && thereAreRepacks)
2014  {
2015    #ifdef Z7_EXTERNAL_CODECS
2016    threadDecoder._externalCodecs = _externalCodecs;
2017    #endif
2018    const WRes wres = threadDecoder.Create();
2019    if (wres != 0)
2020      return HRESULT_FROM_WIN32(wres);
2021  }
2022  #endif
2023
2024  {
2025    CAnalysis analysis;
2026    // analysis.Need_ATime = options.Need_ATime;
2027    int analysisLevel = options.AnalysisLevel;
2028    // (analysisLevel < 0) means default level (5)
2029    if (analysisLevel < 0)
2030      analysisLevel = 5;
2031    if (analysisLevel != 0)
2032    {
2033      analysis.Callback = opCallback;
2034      analysis.ParseWav = true;
2035      if (analysisLevel >= 5)
2036      {
2037        analysis.ParseExe = true;
2038        analysis.ParseExeUnix = true;
2039        // analysis.ParseNoExt = true;
2040        if (analysisLevel >= 7)
2041        {
2042          analysis.ParseNoExt = true;
2043          if (analysisLevel >= 9)
2044            analysis.ParseAll = true;
2045        }
2046      }
2047    }
2048
2049    // ---------- Split files to groups ----------
2050
2051    const CCompressionMethodMode &method = *options.Method;
2052
2053    FOR_VECTOR (i, updateItems)
2054    {
2055      const CUpdateItem &ui = updateItems[i];
2056      if (!ui.NewData || !ui.HasStream())
2057        continue;
2058
2059      CFilterMode2 fm;
2060      if (useFilters)
2061      {
2062        // analysis.ATime_Defined = false;
2063        RINOK(analysis.GetFilterGroup(i, ui, fm))
2064        /*
2065        if (analysis.ATime_Defined)
2066        {
2067          ui.ATime = FILETIME_To_UInt64(analysis.ATime);
2068          ui.ATime_WasReadByAnalysis = true;
2069        }
2070        */
2071      }
2072      fm.Encrypted = method.PasswordIsDefined;
2073
2074      const unsigned groupIndex = GetGroup(filters, fm);
2075      while (groupIndex >= groups.Size())
2076        groups.AddNew();
2077      groups[groupIndex].Indices.Add(i);
2078    }
2079  }
2080
2081
2082  #ifndef Z7_NO_CRYPTO
2083
2084  CCryptoGetTextPassword *getPasswordSpec = NULL;
2085  CMyComPtr<ICryptoGetTextPassword> getTextPassword;
2086  if (needEncryptedRepack)
2087  {
2088    getPasswordSpec = new CCryptoGetTextPassword;
2089    getTextPassword = getPasswordSpec;
2090
2091    #ifndef Z7_ST
2092    threadDecoder.getTextPassword = getPasswordSpec;
2093    #endif
2094
2095    if (options.Method->PasswordIsDefined)
2096      getPasswordSpec->Password = options.Method->Password;
2097    else
2098    {
2099      Z7_DECL_CMyComPtr_QI_FROM(
2100          ICryptoGetTextPassword,
2101          getDecoderPassword, updateCallback)
2102      if (!getDecoderPassword)
2103        return E_NOTIMPL;
2104      CMyComBSTR password;
2105      RINOK(getDecoderPassword->CryptoGetTextPassword(&password))
2106      if (password)
2107        getPasswordSpec->Password = password;
2108    }
2109  }
2110
2111  #endif
2112
2113  // ---------- Compress ----------
2114
2115  COutArchive archive;
2116  CArchiveDatabaseOut newDatabase;
2117
2118  RINOK(archive.Create_and_WriteStartPrefix(seqOutStream))
2119
2120  /*
2121  CIntVector treeFolderToArcIndex;
2122  treeFolderToArcIndex.Reserve(treeFolders.Size());
2123  for (i = 0; i < treeFolders.Size(); i++)
2124    treeFolderToArcIndex.Add(-1);
2125  // ---------- Write Tree (only AUX dirs) ----------
2126  for (i = 1; i < treeFolders.Size(); i++)
2127  {
2128    const CTreeFolder &treeFolder = treeFolders[i];
2129    CFileItem file;
2130    CFileItem2 file2;
2131    file2.Init();
2132    int secureID = 0;
2133    if (treeFolder.UpdateItemIndex < 0)
2134    {
2135      // we can store virtual dir item wuthout attrib, but we want all items have attrib.
2136      file.SetAttrib(FILE_ATTRIBUTE_DIRECTORY);
2137      file2.IsAux = true;
2138    }
2139    else
2140    {
2141      const CUpdateItem &ui = updateItems[treeFolder.UpdateItemIndex];
2142      // if item is not dir, then it's parent for alt streams.
2143      // we will write such items later
2144      if (!ui.IsDir)
2145        continue;
2146      secureID = ui.SecureIndex;
2147      if (ui.NewProps)
2148        UpdateItem_To_FileItem(ui, file, file2);
2149      else
2150        GetFile(*db, ui.IndexInArchive, file, file2);
2151    }
2152    file.Size = 0;
2153    file.HasStream = false;
2154    file.IsDir = true;
2155    file.Parent = treeFolder.Parent;
2156
2157    treeFolderToArcIndex[i] = newDatabase.Files.Size();
2158    newDatabase.AddFile(file, file2, treeFolder.Name);
2159
2160    if (totalSecureDataSize != 0)
2161      newDatabase.SecureIDs.Add(secureID);
2162  }
2163  */
2164
2165  {
2166    /* ---------- Write non-AUX dirs and Empty files ---------- */
2167    CUIntVector emptyRefs;
2168
2169    unsigned i;
2170
2171    for (i = 0; i < updateItems.Size(); i++)
2172    {
2173      const CUpdateItem &ui = updateItems[i];
2174      if (ui.NewData)
2175      {
2176        if (ui.HasStream())
2177          continue;
2178      }
2179      else if (ui.IndexInArchive != -1 && db->Files[(unsigned)ui.IndexInArchive].HasStream)
2180        continue;
2181      /*
2182      if (ui.TreeFolderIndex >= 0)
2183        continue;
2184      */
2185      emptyRefs.Add(i);
2186    }
2187
2188    emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems);
2189
2190    for (i = 0; i < emptyRefs.Size(); i++)
2191    {
2192      const CUpdateItem &ui = updateItems[emptyRefs[i]];
2193      CFileItem file;
2194      CFileItem2 file2;
2195      UString name;
2196      if (ui.NewProps)
2197      {
2198        UpdateItem_To_FileItem(ui, file, file2);
2199        file.CrcDefined = false;
2200        name = ui.Name;
2201      }
2202      else
2203      {
2204        GetFile(*db, (unsigned)ui.IndexInArchive, file, file2);
2205        db->GetPath((unsigned)ui.IndexInArchive, name);
2206      }
2207
2208      /*
2209      if (totalSecureDataSize != 0)
2210        newDatabase.SecureIDs.Add(ui.SecureIndex);
2211      file.Parent = ui.ParentFolderIndex;
2212      */
2213      newDatabase.AddFile(file, file2, name);
2214    }
2215  }
2216
2217  lps->ProgressOffset = 0;
2218
2219  {
2220    // ---------- Sort Filters ----------
2221    FOR_VECTOR (i, filters)
2222    {
2223      filters[i].GroupIndex = i;
2224    }
2225    filters.Sort2();
2226  }
2227
2228  for (unsigned groupIndex = 0; groupIndex < filters.Size(); groupIndex++)
2229  {
2230    const CFilterMode2 &filterMode = filters[groupIndex];
2231
2232    CCompressionMethodMode method = *options.Method;
2233    {
2234      const HRESULT res = MakeExeMethod(method, filterMode,
2235        #ifdef Z7_ST
2236          false
2237        #else
2238          options.MaxFilter && options.MultiThreadMixer
2239        #endif
2240        );
2241
2242      RINOK(res)
2243    }
2244
2245    if (filterMode.Encrypted)
2246    {
2247      if (!method.PasswordIsDefined)
2248      {
2249        #ifndef Z7_NO_CRYPTO
2250        if (getPasswordSpec)
2251          method.Password = getPasswordSpec->Password;
2252        #endif
2253        method.PasswordIsDefined = true;
2254      }
2255    }
2256    else
2257    {
2258      method.PasswordIsDefined = false;
2259      method.Password.Empty();
2260    }
2261
2262    CEncoder encoder(method);
2263
2264    // ---------- Repack and copy old solid blocks ----------
2265
2266    const CSolidGroup &group = groups[filterMode.GroupIndex];
2267
2268    FOR_VECTOR (folderRefIndex, group.folderRefs)
2269    {
2270      const CFolderRepack &rep = group.folderRefs[folderRefIndex];
2271
2272      const unsigned folderIndex = rep.FolderIndex;
2273
2274      const CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex];
2275
2276      if (rep.NumCopyFiles == numUnpackStreams)
2277      {
2278        if (opCallback)
2279        {
2280          RINOK(opCallback->ReportOperation(
2281              NEventIndexType::kBlockIndex, (UInt32)folderIndex,
2282              NUpdateNotifyOp::kReplicate))
2283
2284          // ---------- Copy old solid block ----------
2285          {
2286            CNum indexInFolder = 0;
2287            for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++)
2288            {
2289              if (db->Files[fi].HasStream)
2290              {
2291                indexInFolder++;
2292                RINOK(opCallback->ReportOperation(
2293                    NEventIndexType::kInArcIndex, (UInt32)fi,
2294                    NUpdateNotifyOp::kReplicate))
2295              }
2296            }
2297          }
2298        }
2299
2300        const UInt64 packSize = db->GetFolderFullPackSize(folderIndex);
2301        RINOK(WriteRange(inStream, archive.SeqStream,
2302            db->GetFolderStreamPos(folderIndex, 0), packSize, progress))
2303        lps->ProgressOffset += packSize;
2304
2305        const unsigned folderIndex_New = newDatabase.Folders.Size();
2306        CFolder &folder = newDatabase.Folders.AddNew();
2307        // v23.01: we copy FolderCrc, if FolderCrc was used
2308        if (db->FolderCRCs.ValidAndDefined(folderIndex))
2309          newDatabase.FolderUnpackCRCs.SetItem(folderIndex_New,
2310              true, db->FolderCRCs.Vals[folderIndex]);
2311
2312        db->ParseFolderInfo(folderIndex, folder);
2313        const CNum startIndex = db->FoStartPackStreamIndex[folderIndex];
2314        FOR_VECTOR (j, folder.PackStreams)
2315        {
2316          newDatabase.PackSizes.Add(db->GetStreamPackSize(startIndex + j));
2317          // newDatabase.PackCRCsDefined.Add(db.PackCRCsDefined[startIndex + j]);
2318          // newDatabase.PackCRCs.Add(db.PackCRCs[startIndex + j]);
2319        }
2320
2321        size_t indexStart = db->FoToCoderUnpackSizes[folderIndex];
2322        const size_t indexEnd = db->FoToCoderUnpackSizes[folderIndex + 1];
2323        for (; indexStart < indexEnd; indexStart++)
2324          newDatabase.CoderUnpackSizes.Add(db->CoderUnpackSizes[indexStart]);
2325      }
2326      else
2327      {
2328        // ---------- Repack old solid block ----------
2329
2330        CBoolVector extractStatuses;
2331
2332        CNum indexInFolder = 0;
2333
2334        if (opCallback)
2335        {
2336          RINOK(opCallback->ReportOperation(
2337              NEventIndexType::kBlockIndex, (UInt32)folderIndex,
2338              NUpdateNotifyOp::kRepack))
2339        }
2340
2341        /* We could reduce data size of decoded folder, if we don't need to repack
2342           last files in folder. But the gain in speed is small in most cases.
2343           So we unpack full folder. */
2344
2345        UInt64 sizeToEncode = 0;
2346
2347        /*
2348        UInt64 importantUnpackSize = 0;
2349        unsigned numImportantFiles = 0;
2350        UInt64 decodeSize = 0;
2351        */
2352
2353        for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++)
2354        {
2355          bool needExtract = false;
2356          const CFileItem &file = db->Files[fi];
2357
2358          if (file.HasStream)
2359          {
2360            indexInFolder++;
2361            const int updateIndex = fileIndexToUpdateIndexMap[fi];
2362            if (updateIndex >= 0 && !updateItems[(unsigned)updateIndex].NewData)
2363              needExtract = true;
2364            // decodeSize += file.Size;
2365          }
2366
2367          extractStatuses.Add(needExtract);
2368          if (needExtract)
2369          {
2370            sizeToEncode += file.Size;
2371            /*
2372            numImportantFiles = extractStatuses.Size();
2373            importantUnpackSize = decodeSize;
2374            */
2375          }
2376        }
2377
2378        // extractStatuses.DeleteFrom(numImportantFiles);
2379
2380        unsigned startPackIndex = newDatabase.PackSizes.Size();
2381        UInt64 curUnpackSize;
2382        {
2383          CMyComPtr<ISequentialInStream> sbInStream;
2384          CRepackStreamBase *repackBase;
2385          CFolderInStream2 *FosSpec2 = NULL;
2386
2387          CRepackInStreamWithSizes *inStreamSizeCountSpec = new CRepackInStreamWithSizes;
2388          CMyComPtr<ISequentialInStream> inStreamSizeCount = inStreamSizeCountSpec;
2389          {
2390            #ifndef Z7_ST
2391            if (options.MultiThreadMixer)
2392            {
2393              repackBase = threadDecoder.FosSpec;
2394              CMyComPtr<ISequentialOutStream> sbOutStream;
2395              sb.CreateStreams2(sbInStream, sbOutStream);
2396              RINOK(sb.Create_ReInit())
2397
2398              threadDecoder.FosSpec->_stream = sbOutStream;
2399
2400              threadDecoder.InStream = inStream;
2401              threadDecoder.StartPos = db->ArcInfo.DataStartPosition; // db->GetFolderStreamPos(folderIndex, 0);
2402              threadDecoder.Folders = (const CFolders *)db;
2403              threadDecoder.FolderIndex = folderIndex;
2404
2405              // threadDecoder.UnpackSize = importantUnpackSize;
2406              // threadDecoder.send_UnpackSize = true;
2407            }
2408            else
2409            #endif
2410            {
2411              FosSpec2 = new CFolderInStream2;
2412              FosSpec2->Init();
2413              sbInStream = FosSpec2;
2414              repackBase = FosSpec2;
2415
2416              #ifndef Z7_NO_CRYPTO
2417              bool isEncrypted = false;
2418              bool passwordIsDefined = false;
2419              UString password;
2420              #endif
2421
2422              CMyComPtr<ISequentialInStream> decodedStream;
2423              bool dataAfterEnd_Error = false;
2424
2425              const HRESULT res = threadDecoder.Decoder.Decode(
2426                  EXTERNAL_CODECS_LOC_VARS
2427                  inStream,
2428                  db->ArcInfo.DataStartPosition, // db->GetFolderStreamPos(folderIndex, 0);,
2429                  *db, folderIndex,
2430                  // &importantUnpackSize, // *unpackSize
2431                  NULL, // *unpackSize : FULL unpack
2432
2433                  NULL, // *outStream
2434                  NULL, // *compressProgress
2435
2436                  &decodedStream
2437                  , dataAfterEnd_Error
2438
2439                  Z7_7Z_DECODER_CRYPRO_VARS
2440                  #ifndef Z7_ST
2441                    , false // mtMode
2442                    , 1 // numThreads
2443                    , 0 // memUsage
2444                  #endif
2445                );
2446
2447              RINOK(res)
2448              if (!decodedStream)
2449                return E_FAIL;
2450
2451              FosSpec2->_inStream = decodedStream;
2452            }
2453
2454            repackBase->_db = db;
2455            repackBase->_opCallback = opCallback;
2456            repackBase->_extractCallback = extractCallback;
2457
2458            UInt32 startIndex = db->FolderStartFileIndex[folderIndex];
2459            RINOK(repackBase->Init(startIndex, &extractStatuses))
2460
2461            inStreamSizeCountSpec->_db = db;
2462            inStreamSizeCountSpec->Init(sbInStream, startIndex, &extractStatuses);
2463
2464            #ifndef Z7_ST
2465            if (options.MultiThreadMixer)
2466            {
2467              WRes wres = threadDecoder.Start();
2468              if (wres != 0)
2469                return HRESULT_FROM_WIN32(wres);
2470            }
2471            #endif
2472          }
2473
2474          // curUnpackSize = sizeToEncode;
2475
2476          HRESULT encodeRes = encoder.Encode1(
2477              EXTERNAL_CODECS_LOC_VARS
2478              inStreamSizeCount,
2479              // NULL,
2480              &inSizeForReduce,
2481              sizeToEncode, // expectedDataSize
2482              newDatabase.Folders.AddNew(),
2483              // newDatabase.CoderUnpackSizes, curUnpackSize,
2484              archive.SeqStream, newDatabase.PackSizes, progress);
2485
2486          if (encodeRes == k_My_HRESULT_CRC_ERROR)
2487            return E_FAIL;
2488
2489          curUnpackSize = inStreamSizeCountSpec->GetSize();
2490
2491          if (encodeRes == S_OK)
2492          {
2493            encoder.Encode_Post(curUnpackSize, newDatabase.CoderUnpackSizes);
2494          }
2495
2496          #ifndef Z7_ST
2497          if (options.MultiThreadMixer)
2498          {
2499            // 16.00: hang was fixed : for case if decoding was not finished.
2500            // We close CBinderInStream and it calls CStreamBinder::CloseRead()
2501            inStreamSizeCount.Release();
2502            sbInStream.Release();
2503
2504            {
2505              const WRes wres = threadDecoder.WaitExecuteFinish();
2506              if (wres != 0)
2507                return HRESULT_FROM_WIN32(wres);
2508            }
2509
2510            const HRESULT decodeRes = threadDecoder.Result;
2511            // if (res == k_My_HRESULT_CRC_ERROR)
2512            if (decodeRes == S_FALSE || threadDecoder.dataAfterEnd_Error)
2513            {
2514              if (extractCallback)
2515              {
2516                RINOK(extractCallback->ReportExtractResult(
2517                    NEventIndexType::kInArcIndex, db->FolderStartFileIndex[folderIndex],
2518                    // NEventIndexType::kBlockIndex, (UInt32)folderIndex,
2519                    (decodeRes != S_OK ?
2520                      NExtract::NOperationResult::kDataError :
2521                      NExtract::NOperationResult::kDataAfterEnd)))
2522              }
2523              if (decodeRes != S_OK)
2524                return E_FAIL;
2525            }
2526            RINOK(decodeRes)
2527            if (encodeRes == S_OK)
2528              if (sb.ProcessedSize != sizeToEncode)
2529                encodeRes = E_FAIL;
2530          }
2531          else
2532          #endif
2533          {
2534            if (FosSpec2->Result == S_FALSE)
2535            {
2536              if (extractCallback)
2537              {
2538                RINOK(extractCallback->ReportExtractResult(
2539                    NEventIndexType::kBlockIndex, (UInt32)folderIndex,
2540                    NExtract::NOperationResult::kDataError))
2541              }
2542              return E_FAIL;
2543            }
2544            RINOK(FosSpec2->Result)
2545          }
2546
2547          RINOK(encodeRes)
2548          RINOK(repackBase->CheckFinishedState())
2549
2550          if (curUnpackSize != sizeToEncode)
2551            return E_FAIL;
2552        }
2553
2554        for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)
2555          lps->OutSize += newDatabase.PackSizes[startPackIndex];
2556        lps->InSize += curUnpackSize;
2557      }
2558
2559      newDatabase.NumUnpackStreamsVector.Add(rep.NumCopyFiles);
2560
2561      CNum indexInFolder = 0;
2562      for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++)
2563      {
2564        if (db->Files[fi].HasStream)
2565        {
2566          indexInFolder++;
2567          const int updateIndex = fileIndexToUpdateIndexMap[fi];
2568          if (updateIndex >= 0)
2569          {
2570            const CUpdateItem &ui = updateItems[(unsigned)updateIndex];
2571            if (ui.NewData)
2572              continue;
2573
2574            UString name;
2575            CFileItem file;
2576            CFileItem2 file2;
2577            GetFile(*db, fi, file, file2);
2578
2579            if (ui.NewProps)
2580            {
2581              UpdateItem_To_FileItem2(ui, file2);
2582              file.IsDir = ui.IsDir;
2583              name = ui.Name;
2584            }
2585            else
2586              db->GetPath(fi, name);
2587
2588            /*
2589            file.Parent = ui.ParentFolderIndex;
2590            if (ui.TreeFolderIndex >= 0)
2591              treeFolderToArcIndex[ui.TreeFolderIndex] = newDatabase.Files.Size();
2592            if (totalSecureDataSize != 0)
2593              newDatabase.SecureIDs.Add(ui.SecureIndex);
2594            */
2595            newDatabase.AddFile(file, file2, name);
2596          }
2597        }
2598      }
2599    }
2600
2601
2602    // ---------- Compress files to new solid blocks ----------
2603
2604    const unsigned numFiles = group.Indices.Size();
2605    if (numFiles == 0)
2606      continue;
2607    CRecordVector<CRefItem> refItems;
2608    refItems.ClearAndSetSize(numFiles);
2609    // bool sortByType = (options.UseTypeSorting && isSoid); // numSolidFiles > 1
2610    const bool sortByType = options.UseTypeSorting;
2611
2612    unsigned i;
2613
2614    for (i = 0; i < numFiles; i++)
2615      refItems[i] = CRefItem(group.Indices[i], updateItems[group.Indices[i]], sortByType);
2616
2617    CSortParam sortParam;
2618    // sortParam.TreeFolders = &treeFolders;
2619    sortParam.SortByType = sortByType;
2620    refItems.Sort(CompareUpdateItems, (void *)&sortParam);
2621
2622    CObjArray<UInt32> indices(numFiles);
2623
2624    for (i = 0; i < numFiles; i++)
2625    {
2626      const UInt32 index = refItems[i].Index;
2627      indices[i] = index;
2628      /*
2629      const CUpdateItem &ui = updateItems[index];
2630      CFileItem file;
2631      if (ui.NewProps)
2632        UpdateItem_To_FileItem(ui, file);
2633      else
2634        file = db.Files[ui.IndexInArchive];
2635      if (file.IsAnti || file.IsDir)
2636        return E_FAIL;
2637      newDatabase.Files.Add(file);
2638      */
2639    }
2640
2641    for (i = 0; i < numFiles;)
2642    {
2643      UInt64 totalSize = 0;
2644      unsigned numSubFiles;
2645
2646      const wchar_t *prevExtension = NULL;
2647
2648      for (numSubFiles = 0; i + numSubFiles < numFiles && numSubFiles < numSolidFiles; numSubFiles++)
2649      {
2650        const CUpdateItem &ui = updateItems[indices[i + numSubFiles]];
2651        totalSize += ui.Size;
2652        if (totalSize > options.NumSolidBytes)
2653          break;
2654        if (options.SolidExtension)
2655        {
2656          const int slashPos = ui.Name.ReverseFind_PathSepar();
2657          const int dotPos = ui.Name.ReverseFind_Dot();
2658          const wchar_t *ext = ui.Name.Ptr(dotPos <= slashPos ? ui.Name.Len() : (unsigned)(dotPos + 1));
2659          if (numSubFiles == 0)
2660            prevExtension = ext;
2661          else if (!StringsAreEqualNoCase(ext, prevExtension))
2662            break;
2663        }
2664      }
2665
2666      if (numSubFiles < 1)
2667        numSubFiles = 1;
2668
2669      RINOK(lps->SetCur())
2670
2671      /*
2672      const unsigned folderIndex = newDatabase.NumUnpackStreamsVector.Size();
2673
2674      if (opCallback)
2675      {
2676        RINOK(opCallback->ReportOperation(
2677            NEventIndexType::kBlockIndex, (UInt32)folderIndex,
2678            NUpdateNotifyOp::kAdd));
2679      }
2680      */
2681
2682
2683      CFolderInStream *inStreamSpec = new CFolderInStream;
2684      CMyComPtr<ISequentialInStream> solidInStream(inStreamSpec);
2685
2686      // inStreamSpec->_reportArcProp = reportArcProp;
2687
2688      inStreamSpec->Need_CTime = options.Need_CTime;
2689      inStreamSpec->Need_ATime = options.Need_ATime;
2690      inStreamSpec->Need_MTime = options.Need_MTime;
2691      inStreamSpec->Need_Attrib = options.Need_Attrib;
2692      // inStreamSpec->Need_Crc = options.Need_Crc;
2693
2694      inStreamSpec->Init(updateCallback, &indices[i], numSubFiles);
2695
2696      unsigned startPackIndex = newDatabase.PackSizes.Size();
2697      // UInt64 curFolderUnpackSize = totalSize;
2698      // curFolderUnpackSize = (UInt64)(Int64)-1; // for debug
2699      const UInt64 expectedDataSize = totalSize;
2700
2701      // const unsigned folderIndex_New = newDatabase.Folders.Size();
2702
2703      RINOK(encoder.Encode1(
2704          EXTERNAL_CODECS_LOC_VARS
2705          solidInStream,
2706          // NULL,
2707          &inSizeForReduce,
2708          expectedDataSize, // expected size
2709          newDatabase.Folders.AddNew(),
2710          // newDatabase.CoderUnpackSizes, curFolderUnpackSize,
2711          archive.SeqStream, newDatabase.PackSizes, progress))
2712
2713      if (!inStreamSpec->WasFinished())
2714        return E_FAIL;
2715
2716      /*
2717      if (inStreamSpec->Need_FolderCrc)
2718        newDatabase.FolderUnpackCRCs.SetItem(folderIndex_New,
2719            true, inStreamSpec->GetFolderCrc());
2720      */
2721
2722      const UInt64 curFolderUnpackSize = inStreamSpec->Get_TotalSize_for_Coder();
2723      encoder.Encode_Post(curFolderUnpackSize, newDatabase.CoderUnpackSizes);
2724
2725      UInt64 packSize = 0;
2726      // const UInt32 numStreams = newDatabase.PackSizes.Size() - startPackIndex;
2727      for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)
2728        packSize += newDatabase.PackSizes[startPackIndex];
2729      lps->OutSize += packSize;
2730
2731      // for ()
2732      // newDatabase.PackCRCsDefined.Add(false);
2733      // newDatabase.PackCRCs.Add(0);
2734
2735      CNum numUnpackStreams = 0;
2736      UInt64 skippedSize = 0;
2737      UInt64 procSize = 0;
2738      // unsigned numProcessedFiles = 0;
2739
2740      for (unsigned subIndex = 0; subIndex < numSubFiles; subIndex++)
2741      {
2742        const CUpdateItem &ui = updateItems[indices[i + subIndex]];
2743        CFileItem file;
2744        CFileItem2 file2;
2745        UString name;
2746        if (ui.NewProps)
2747        {
2748          UpdateItem_To_FileItem(ui, file, file2);
2749          name = ui.Name;
2750        }
2751        else
2752        {
2753          GetFile(*db, (unsigned)ui.IndexInArchive, file, file2);
2754          db->GetPath((unsigned)ui.IndexInArchive, name);
2755        }
2756        if (file2.IsAnti || file.IsDir)
2757          return E_FAIL;
2758
2759        /*
2760        CFileItem &file = newDatabase.Files[
2761              startFileIndexInDatabase + i + subIndex];
2762        */
2763        if (!inStreamSpec->Processed[subIndex])
2764        {
2765          // we don't add file here
2766          skippedSize += ui.Size;
2767          continue; // comment it for debug
2768          // name += ".locked"; // for debug
2769        }
2770
2771        // if (inStreamSpec->Need_Crc)
2772        file.Crc = inStreamSpec->CRCs[subIndex];
2773        file.Size = inStreamSpec->Sizes[subIndex];
2774
2775        procSize += file.Size;
2776        // if (file.Size >= 0) // for debug: test purposes
2777        if (file.Size != 0)
2778        {
2779          file.CrcDefined = true; // inStreamSpec->Need_Crc;
2780          file.HasStream = true;
2781          numUnpackStreams++;
2782        }
2783        else
2784        {
2785          file.CrcDefined = false;
2786          file.HasStream = false;
2787        }
2788
2789        if (inStreamSpec->TimesDefined[subIndex])
2790        {
2791          if (inStreamSpec->Need_CTime)
2792            { file2.CTimeDefined = true;  file2.CTime = inStreamSpec->CTimes[subIndex]; }
2793          if (inStreamSpec->Need_ATime
2794              // && !ui.ATime_WasReadByAnalysis
2795              )
2796            { file2.ATimeDefined = true;  file2.ATime = inStreamSpec->ATimes[subIndex]; }
2797          if (inStreamSpec->Need_MTime)
2798            { file2.MTimeDefined = true;  file2.MTime = inStreamSpec->MTimes[subIndex]; }
2799          if (inStreamSpec->Need_Attrib)
2800          {
2801            file2.AttribDefined = true;
2802            file2.Attrib = inStreamSpec->Attribs[subIndex];
2803          }
2804        }
2805
2806        /*
2807        file.Parent = ui.ParentFolderIndex;
2808        if (ui.TreeFolderIndex >= 0)
2809          treeFolderToArcIndex[ui.TreeFolderIndex] = newDatabase.Files.Size();
2810        if (totalSecureDataSize != 0)
2811          newDatabase.SecureIDs.Add(ui.SecureIndex);
2812        */
2813        /*
2814        if (reportArcProp)
2815        {
2816          RINOK(ReportItemProps(reportArcProp, ui.IndexInClient, file.Size,
2817              file.CrcDefined ? &file.Crc : NULL))
2818        }
2819        */
2820
2821        // numProcessedFiles++;
2822        newDatabase.AddFile(file, file2, name);
2823      }
2824
2825      /*
2826      // for debug:
2827      // we can write crc to folders area, if folder contains only one file
2828      if (numUnpackStreams == 1 && numSubFiles == 1)
2829      {
2830        const CFileItem &file = newDatabase.Files.Back();
2831        if (file.CrcDefined)
2832          newDatabase.FolderUnpackCRCs.SetItem(folderIndex_New, true, file.Crc);
2833      }
2834      */
2835
2836      /*
2837      // it's optional check to ensure that sizes are correct
2838      if (inStreamSpec->TotalSize_for_Coder != curFolderUnpackSize)
2839        return E_FAIL;
2840      */
2841      // if (inStreamSpec->AlignLog == 0)
2842      {
2843        if (procSize != curFolderUnpackSize)
2844          return E_FAIL;
2845      }
2846      // else
2847      {
2848        /*
2849        {
2850          const CFolder &old = newDatabase.Folders.Back();
2851          CFolder &folder = newDatabase.Folders.AddNew();
2852          {
2853            const unsigned numBonds = old.Bonds.Size();
2854            folder.Bonds.SetSize(numBonds + 1);
2855            for (unsigned k = 0; k < numBonds; k++)
2856              folder.Bonds[k] = old.Bonds[k];
2857            CBond &bond = folder.Bonds[numBonds];
2858            bond.PackIndex = 0;
2859            bond.UnpackIndex = 0;
2860          }
2861          {
2862            const unsigned numCoders = old.Coders.Size();
2863            folder.Coders.SetSize(numCoders + 1);
2864            for (unsigned k = 0; k < numCoders; k++)
2865              folder.Coders[k] = old.Coders[k];
2866            CCoderInfo &cod = folder.Coders[numCoders];
2867            cod.Props.Alloc(1);
2868            cod.Props[0] = (Byte)inStreamSpec->AlignLog;
2869            cod.NumStreams = 1;
2870          }
2871          {
2872            const unsigned numPackStreams = old.Coders.Size();
2873            folder.Coders.SetSize(numPackStreams);
2874            for (unsigned k = 0; k < numPackStreams; k++)
2875              folder.PackStreams[k] = old.PackStreams[k];
2876          }
2877        }
2878        newDatabase.Folders.Delete(newDatabase.Folders.Size() - 2);
2879        */
2880      }
2881
2882
2883      lps->InSize += procSize;
2884      // lps->InSize += curFolderUnpackSize;
2885
2886      // numUnpackStreams = 0 is very bad case for locked files
2887      // v3.13 doesn't understand it.
2888      newDatabase.NumUnpackStreamsVector.Add(numUnpackStreams);
2889      i += numSubFiles;
2890
2891      if (skippedSize != 0 && complexity >= skippedSize)
2892      {
2893        complexity -= skippedSize;
2894        RINOK(updateCallback->SetTotal(complexity))
2895      }
2896
2897      /*
2898      if (reportArcProp)
2899      {
2900        PROPVARIANT prop;
2901        prop.vt = VT_EMPTY;
2902        prop.wReserved1 = 0;
2903        {
2904          NWindows::NCOM::PropVarEm_Set_UInt32(&prop, numProcessedFiles);
2905          RINOK(reportArcProp->ReportProp(
2906              NEventIndexType::kBlockIndex, (UInt32)folderIndex, kpidNumSubFiles, &prop));
2907        }
2908        {
2909          NWindows::NCOM::PropVarEm_Set_UInt64(&prop, curFolderUnpackSize);
2910          RINOK(reportArcProp->ReportProp(
2911              NEventIndexType::kBlockIndex, (UInt32)folderIndex, kpidSize, &prop));
2912        }
2913        {
2914          NWindows::NCOM::PropVarEm_Set_UInt64(&prop, packSize);
2915          RINOK(reportArcProp->ReportProp(
2916              NEventIndexType::kBlockIndex, (UInt32)folderIndex, kpidPackSize, &prop));
2917        }
2918        {
2919          NWindows::NCOM::PropVarEm_Set_UInt32(&prop, numStreams);
2920          RINOK(reportArcProp->ReportProp(
2921              NEventIndexType::kBlockIndex, (UInt32)folderIndex, kpidNumStreams, &prop));
2922        }
2923        RINOK(reportArcProp->ReportFinished(NEventIndexType::kBlockIndex, (UInt32)folderIndex, NUpdate::NOperationResult::kOK));
2924      }
2925      */
2926      /*
2927      if (opCallback)
2928      {
2929        RINOK(opCallback->ReportOperation(
2930            NEventIndexType::kBlockIndex, (UInt32)folderIndex,
2931            NUpdateNotifyOp::kOpFinished));
2932      }
2933      */
2934    }
2935  }
2936
2937  RINOK(lps->SetCur())
2938
2939  /*
2940  fileIndexToUpdateIndexMap.ClearAndFree();
2941  groups.ClearAndFree();
2942  */
2943
2944  /*
2945  for (i = 0; i < newDatabase.Files.Size(); i++)
2946  {
2947    CFileItem &file = newDatabase.Files[i];
2948    file.Parent = treeFolderToArcIndex[file.Parent];
2949  }
2950
2951  if (totalSecureDataSize != 0)
2952  {
2953    newDatabase.SecureBuf.SetCapacity(totalSecureDataSize);
2954    size_t pos = 0;
2955    newDatabase.SecureSizes.Reserve(secureBlocks.Sorted.Size());
2956    for (i = 0; i < secureBlocks.Sorted.Size(); i++)
2957    {
2958      const CByteBuffer &buf = secureBlocks.Bufs[secureBlocks.Sorted[i]];
2959      size_t size = buf.GetCapacity();
2960      if (size != 0)
2961        memcpy(newDatabase.SecureBuf + pos, buf, size);
2962      newDatabase.SecureSizes.Add((UInt32)size);
2963      pos += size;
2964    }
2965  }
2966  */
2967
2968  {
2969    const unsigned numFolders = newDatabase.Folders.Size();
2970    if (newDatabase.NumUnpackStreamsVector.Size() != numFolders
2971        || newDatabase.FolderUnpackCRCs.Defs.Size() > numFolders)
2972      return E_FAIL;
2973    newDatabase.FolderUnpackCRCs.if_NonEmpty_FillResedue_with_false(numFolders);
2974  }
2975
2976  updateItems.ClearAndFree();
2977  newDatabase.ReserveDown();
2978
2979  if (opCallback)
2980    RINOK(opCallback->ReportOperation(NEventIndexType::kNoIndex, (UInt32)(Int32)-1, NUpdateNotifyOp::kHeader))
2981
2982  RINOK(archive.WriteDatabase(EXTERNAL_CODECS_LOC_VARS
2983      newDatabase, options.HeaderMethod, options.HeaderOptions))
2984
2985  if (v_StreamSetRestriction)
2986    RINOK(v_StreamSetRestriction->SetRestriction(0, 0))
2987
2988  return S_OK;
2989}
2990
2991}}
2992