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