• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
26 namespace NArchive {
27 namespace N7z {
28 
29 #define k_X86 k_BCJ
30 
31 struct 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 
CFilterModeNArchive::N7z::CFilterMode39   CFilterMode():
40     Id(0),
41     Delta(0),
42     Offset(0)
43     // , AlignSizeOpt(0)
44     {}
45 
ClearFilterModeNArchive::N7z::CFilterMode46   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
SetDeltaNArchive::N7z::CFilterMode56   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 
Parse_EXE(const Byte * buf,size_t size,CFilterMode * filterMode)82 static 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 
Get16(const Byte * p,BoolInt be)207 static UInt16 Get16(const Byte *p, BoolInt be) { if (be) return (UInt16)GetBe16(p); return (UInt16)GetUi16(p); }
Get32(const Byte * p,BoolInt be)208 static 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 
Parse_ELF(const Byte * buf,size_t size,CFilterMode * filterMode)211 static 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 
Parse_MACH(const Byte * buf,size_t size,CFilterMode * filterMode)278 static 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 
Parse_WAV(const Byte * buf,size_t size,CFilterMode * filterMode)325 static 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 */
ParseFile(const Byte * buf,size_t size,CFilterMode * filterMode)376 static 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 
389 struct CFilterMode2: public CFilterMode
390 {
391   bool Encrypted;
392   unsigned GroupIndex;
393 
CFilterMode2NArchive::N7z::CFilterMode2394   CFilterMode2(): Encrypted(false) {}
395 
CompareNArchive::N7z::CFilterMode2396   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 
operator ==NArchive::N7z::CFilterMode2438   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 
GetGroup(CRecordVector<CFilterMode2> & filters,const CFilterMode2 & m)447 static 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 
Is86Filter(CMethodId m)475 static inline bool Is86Filter(CMethodId m)
476 {
477   return (m == k_BCJ || m == k_BCJ2);
478 }
479 
IsExeFilter(CMethodId m)480 static 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 
Get_FilterGroup_for_Folder(CRecordVector<CFilterMode2> & filters,const CFolderEx & f,bool extractFilter)497 static 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 
WriteRange(IInStream * inStream,ISequentialOutStream * outStream,UInt64 position,UInt64 size,ICompressProgressInfo * progress)536 static 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 /*
552 unsigned 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 
561 UString 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 /*
572 static 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 
582 static 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 
590 static 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 
596 static 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 /*
614 static int CompareFiles(const CFileItem &f1, const CFileItem &f2)
615 {
616   return CompareFileNames(f1.Name, f2.Name);
617 }
618 */
619 
620 struct CFolderRepack
621 {
622   unsigned FolderIndex;
623   CNum NumCopyFiles;
624 };
625 
626 /*
627 static 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 
CompareEmptyItems(const unsigned * p1,const unsigned * p2,void * param)659 static 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 
678 static 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 
GetExtIndex(const char * ext)711 static 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 
744 struct CRefItem
745 {
746   const CUpdateItem *UpdateItem;
747   UInt32 Index;
748   unsigned ExtensionPos;
749   unsigned NamePos;
750   unsigned ExtensionIndex;
751 
CRefItemNArchive::N7z::CRefItem752   CRefItem() {}
CRefItemNArchive::N7z::CRefItem753   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 
791 struct 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 
CompareUpdateItems(const CRefItem * p1,const CRefItem * p2,void * param)806 static 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 
893 struct CSolidGroup
894 {
895   CRecordVector<UInt32> Indices;
896 
897   CRecordVector<CFolderRepack> folderRefs;
898 };
899 
900 static const char * const g_Exe_Exts[] =
901 {
902     "dll"
903   , "exe"
904   , "ocx"
905   , "sfx"
906   , "sys"
907 };
908 
909 static const char * const g_ExeUnix_Exts[] =
910 {
911     "so"
912   , "dylib"
913 };
914 
IsExt_Exe(const wchar_t * ext)915 static 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 /*
924 static 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
IsExt_ExeUnix_NumericAllowed(const UString & path)934 static 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 
968 struct 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 
CAnalysisNArchive::N7z::CAnalysis985   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 
1000 static const size_t kAnalysisBufSize = 1 << 14;
1001 
GetFilterGroup(UInt32 index,const CUpdateItem & ui,CFilterMode & filterMode)1002 HRESULT 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 
GetMethodFull(UInt64 methodID,UInt32 numStreams,CMethodFull & m)1169 static inline void GetMethodFull(UInt64 methodID, UInt32 numStreams, CMethodFull &m)
1170 {
1171   m.Id = methodID;
1172   m.NumStreams = numStreams;
1173 }
1174 
AddBondForFilter(CCompressionMethodMode & mode)1175 static 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 
AddFilterBond(CCompressionMethodMode & mode)1192 static HRESULT AddFilterBond(CCompressionMethodMode &mode)
1193 {
1194   if (!mode.Bonds.IsEmpty())
1195     return AddBondForFilter(mode);
1196   return S_OK;
1197 }
1198 
AddBcj2Methods(CCompressionMethodMode & mode)1199 static 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 
MakeExeMethod(CCompressionMethodMode & mode,const CFilterMode & filterMode,bool bcj2Filter)1238 static 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 
UpdateItem_To_FileItem2(const CUpdateItem & ui,CFileItem2 & file2)1338 static 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 
UpdateItem_To_FileItem(const CUpdateItem & ui,CFileItem & file,CFileItem2 & file2)1351 static 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 
1364 Z7_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;
1373 public:
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 
1386 Z7_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 
1396 Z7_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 
1412 class CRepackStreamBase
1413 {
1414 protected:
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 
1429 public:
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 
1438 HRESULT 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 
1449 HRESULT 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 
1472 const HRESULT k_My_HRESULT_CRC_ERROR = 0x20000002;
1473 
1474 HRESULT 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 
1493 HRESULT 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 
1507 class 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)
1514 public:
1515   CMyComPtr<ISequentialOutStream> _stream;
1516 };
1517 
1518 Z7_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 
1565 static const UInt32 kTempBufSize = 1 << 16;
1566 
1567 class 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;
1576 public:
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 
1594 Z7_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 
1656 class CThreadDecoder Z7_final
1657   #ifndef Z7_ST
1658     : public CVirtThread
1659   #endif
1660 {
1661 public:
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   }
1717 private:
1718   virtual void Execute() Z7_override;
1719 
1720   #endif
1721 };
1722 
1723 #ifndef Z7_ST
1724 
1725 void 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 
1776 Z7_CLASS_IMP_NOQIB_1(
1777   CCryptoGetTextPassword
1778   , ICryptoGetTextPassword
1779 )
1780 public:
1781   UString Password;
1782 };
1783 
1784 Z7_COM7F_IMF(CCryptoGetTextPassword::CryptoGetTextPassword(BSTR *password))
1785 {
1786   return StringToBstr(Password, password);
1787 }
1788 
1789 #endif
1790 
1791 
1792 static 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 
1804 HRESULT 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