6 #include "7zFolderInStream.h"
11 #include "../../Compress/Copy/CopyCoder.h"
12 #include "../../Common/ProgressUtils.h"
13 #include "../../Common/LimitedStreams.h"
14 #include "../../Common/LimitedStreams.h"
15 #include "../Common/ItemNameUtils.h"
20 static const wchar_t *kMatchFinderForBCJ2_LZMA = L"BT2";
21 static const UInt32 kDictionaryForBCJ2_LZMA = 1 << 20;
22 static const UInt32 kAlgorithmForBCJ2_LZMA = 1;
23 static const UInt32 kNumFastBytesForBCJ2_LZMA = 64;
25 static HRESULT WriteRange(IInStream *inStream, ISequentialOutStream *outStream,
26 UInt64 position, UInt64 size, ICompressProgressInfo *progress)
28 RINOK(inStream->Seek(position, STREAM_SEEK_SET, 0));
29 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
30 CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec);
31 streamSpec->SetStream(inStream);
32 streamSpec->Init(size);
34 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
35 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
36 RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress));
37 return (copyCoderSpec->TotalSize == size ? S_OK : E_FAIL);
40 static int GetReverseSlashPos(const UString &name)
42 int slashPos = name.ReverseFind(L'/');
44 int slash1Pos = name.ReverseFind(L'\\');
45 slashPos = MyMax(slashPos, slash1Pos);
50 int CUpdateItem::GetExtensionPos() const
52 int slashPos = GetReverseSlashPos(Name);
53 int dotPos = Name.ReverseFind(L'.');
54 if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0))
59 UString CUpdateItem::GetExtension() const
61 return Name.Mid(GetExtensionPos());
64 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
66 static int CompareBuffers(const CByteBuffer &a1, const CByteBuffer &a2)
68 size_t c1 = a1.GetCapacity();
69 size_t c2 = a2.GetCapacity();
70 RINOZ(MyCompare(c1, c2));
71 for (size_t i = 0; i < c1; i++)
72 RINOZ(MyCompare(a1[i], a2[i]));
76 static int CompareCoders(const CCoderInfo &c1, const CCoderInfo &c2)
78 RINOZ(MyCompare(c1.NumInStreams, c2.NumInStreams));
79 RINOZ(MyCompare(c1.NumOutStreams, c2.NumOutStreams));
80 RINOZ(MyCompare(c1.MethodID, c2.MethodID));
81 return CompareBuffers(c1.Properties, c2.Properties);
84 static int CompareBindPairs(const CBindPair &b1, const CBindPair &b2)
86 RINOZ(MyCompare(b1.InIndex, b2.InIndex));
87 return MyCompare(b1.OutIndex, b2.OutIndex);
90 static int CompareFolders(const CFolder &f1, const CFolder &f2)
92 int s1 = f1.Coders.Size();
93 int s2 = f2.Coders.Size();
94 RINOZ(MyCompare(s1, s2));
96 for (i = 0; i < s1; i++)
97 RINOZ(CompareCoders(f1.Coders[i], f2.Coders[i]));
98 s1 = f1.BindPairs.Size();
99 s2 = f2.BindPairs.Size();
100 RINOZ(MyCompare(s1, s2));
101 for (i = 0; i < s1; i++)
102 RINOZ(CompareBindPairs(f1.BindPairs[i], f2.BindPairs[i]));
106 static int CompareFiles(const CFileItem &f1, const CFileItem &f2)
108 return MyStringCompareNoCase(f1.Name, f2.Name);
111 static int CompareFolderRefs(const int *p1, const int *p2, void *param)
115 const CArchiveDatabaseEx &db = *(const CArchiveDatabaseEx *)param;
116 RINOZ(CompareFolders(
120 db.NumUnPackStreamsVector[i1],
121 db.NumUnPackStreamsVector[i2]));
122 if (db.NumUnPackStreamsVector[i1] == 0)
125 db.Files[db.FolderStartFileIndex[i1]],
126 db.Files[db.FolderStartFileIndex[i2]]);
129 ////////////////////////////////////////////////////////////
131 static int CompareEmptyItems(const int *p1, const int *p2, void *param)
133 const CObjectVector<CUpdateItem> &updateItems = *(const CObjectVector<CUpdateItem> *)param;
134 const CUpdateItem &u1 = updateItems[*p1];
135 const CUpdateItem &u2 = updateItems[*p2];
136 if (u1.IsDirectory != u2.IsDirectory)
137 return (u1.IsDirectory) ? 1 : -1;
140 if (u1.IsAnti != u2.IsAnti)
141 return (u1.IsAnti ? 1 : -1);
142 int n = MyStringCompareNoCase(u1.Name, u2.Name);
145 if (u1.IsAnti != u2.IsAnti)
146 return (u1.IsAnti ? 1 : -1);
147 return MyStringCompareNoCase(u1.Name, u2.Name);
150 static const char *g_Exts =
151 " lzma 7z ace arc arj bz bz2 deb lzo lzx gz pak rpm sit tgz tbz tbz2 tgz cab ha lha lzh rar zoo"
152 " zip jar ear war msi"
153 " 3gp avi mov mpeg mpg mpe wmv"
154 " aac ape fla flac la mp3 m4a mp4 ofr ogg pac ra rm rka shn swa tta wv wma wav"
157 " gif jpeg jpg jp2 png tiff bmp ico psd psp"
158 " awg ps eps cgm dxf svg vrml wmf emf ai md"
159 " cad dwg pps key sxi"
161 " iso bin nrg mdf img pdi tar cpio xpi"
162 " vfd vhd vud vmc vsv"
163 " vmdk dsk nvram vmem vmsd vmsn vmss vmtm"
164 " inl inc idl acf asa h hpp hxx c cpp cxx rc java cs pas bas vb cls ctl frm dlg def"
166 " asm sql manifest dep "
167 " mak clw csproj vcproj sln dsp dsw "
170 " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml"
171 " awk sed hta js php php3 php4 php5 phptml pl pm py pyo rb sh tcl vbs"
172 " text txt tex ans asc srt reg ini doc docx mcw dot rtf hlp xls xlr xlt xlw ppt pdf"
173 " sxc sxd sxi sxg sxw stc sti stw stm odt ott odg otg odp otp ods ots odf"
174 " abw afp cwk lwp wpd wps wpt wrf wri"
175 " abf afm bdf fon mgf otf pcf pfa snf ttf"
176 " dbf mdb nsf ntf wdb db fdb gdb"
177 " exe dll ocx vbx sfx sys tlb awx com obj lib out o so "
178 " pdb pch idb ncb opt";
180 int GetExtIndex(const char *ext)
183 const char *p = g_Exts;
194 char c2 = ext[pos++];
195 if (c2 == 0 && (c == 0 || c == ' '))
216 const CUpdateItem *UpdateItem;
220 CRefItem(UInt32 index, const CUpdateItem &updateItem, bool sortByType):
222 UpdateItem(&updateItem),
229 int slashPos = GetReverseSlashPos(updateItem.Name);
230 NamePos = ((slashPos >= 0) ? (slashPos + 1) : 0);
231 int dotPos = updateItem.Name.ReverseFind(L'.');
232 if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0))
233 ExtensionPos = updateItem.Name.Length();
236 ExtensionPos = dotPos + 1;
237 UString us = updateItem.Name.Mid(ExtensionPos);
243 for (i = 0; i < us.Length(); i++)
250 if (i == us.Length())
251 ExtensionIndex = GetExtIndex(s);
260 static int CompareUpdateItems(const CRefItem *p1, const CRefItem *p2, void *param)
262 const CRefItem &a1 = *p1;
263 const CRefItem &a2 = *p2;
264 const CUpdateItem &u1 = *a1.UpdateItem;
265 const CUpdateItem &u2 = *a2.UpdateItem;
267 if (u1.IsDirectory != u2.IsDirectory)
268 return (u1.IsDirectory) ? 1 : -1;
271 if (u1.IsAnti != u2.IsAnti)
272 return (u1.IsAnti ? 1 : -1);
273 n = MyStringCompareNoCase(u1.Name, u2.Name);
276 bool sortByType = *(bool *)param;
279 RINOZ(MyCompare(a1.ExtensionIndex, a2.ExtensionIndex))
280 RINOZ(MyStringCompareNoCase(u1.Name + a1.ExtensionPos, u2.Name + a2.ExtensionPos));
281 RINOZ(MyStringCompareNoCase(u1.Name + a1.NamePos, u2.Name + a2.NamePos));
282 if (u1.IsLastWriteTimeDefined && u2.IsLastWriteTimeDefined)
283 RINOZ(CompareFileTime(&u1.LastWriteTime, &u2.LastWriteTime));
284 RINOZ(MyCompare(u1.Size, u2.Size))
286 return MyStringCompareNoCase(u1.Name, u2.Name);
291 CCompressionMethodMode Method;
292 CRecordVector<UInt32> Indices;
295 static wchar_t *g_ExeExts[] =
304 static bool IsExeFile(const UString &ext)
306 for (int i = 0; i < sizeof(g_ExeExts) / sizeof(g_ExeExts[0]); i++)
307 if (ext.CompareNoCase(g_ExeExts[i]) == 0)
312 static const UInt64 k_LZMA = 0x030101;
313 static const UInt64 k_BCJ = 0x03030103;
314 static const UInt64 k_BCJ2 = 0x0303011B;
316 static bool GetMethodFull(UInt64 methodID,
317 UInt32 numInStreams, CMethodFull &methodResult)
319 methodResult.Id = methodID;
320 methodResult.NumInStreams = numInStreams;
321 methodResult.NumOutStreams = 1;
325 static bool MakeExeMethod(const CCompressionMethodMode &method,
326 bool bcj2Filter, CCompressionMethodMode &exeMethod)
331 CMethodFull methodFull;
332 if (!GetMethodFull(k_BCJ2, 4, methodFull))
334 exeMethod.Methods.Insert(0, methodFull);
335 if (!GetMethodFull(k_LZMA, 1, methodFull))
339 property.Id = NCoderPropID::kAlgorithm;
340 property.Value = kAlgorithmForBCJ2_LZMA;
341 methodFull.Properties.Add(property);
345 property.Id = NCoderPropID::kMatchFinder;
346 property.Value = kMatchFinderForBCJ2_LZMA;
347 methodFull.Properties.Add(property);
351 property.Id = NCoderPropID::kDictionarySize;
352 property.Value = kDictionaryForBCJ2_LZMA;
353 methodFull.Properties.Add(property);
357 property.Id = NCoderPropID::kNumFastBytes;
358 property.Value = kNumFastBytesForBCJ2_LZMA;
359 methodFull.Properties.Add(property);
362 exeMethod.Methods.Add(methodFull);
363 exeMethod.Methods.Add(methodFull);
371 exeMethod.Binds.Add(bind);
375 exeMethod.Binds.Add(bind);
379 exeMethod.Binds.Add(bind);
383 CMethodFull methodFull;
384 if (!GetMethodFull(k_BCJ, 1, methodFull))
386 exeMethod.Methods.Insert(0, methodFull);
392 exeMethod.Binds.Add(bind);
397 static void SplitFilesToGroups(
398 const CCompressionMethodMode &method,
399 bool useFilters, bool maxFilter,
400 const CObjectVector<CUpdateItem> &updateItems,
401 CObjectVector<CSolidGroup> &groups)
403 if (method.Methods.Size() != 1 || method.Binds.Size() != 0)
406 groups.Add(CSolidGroup());
407 groups.Add(CSolidGroup());
408 CSolidGroup &generalGroup = groups[0];
409 CSolidGroup &exeGroup = groups[1];
410 generalGroup.Method = method;
412 for (i = 0; i < updateItems.Size(); i++)
414 const CUpdateItem &updateItem = updateItems[i];
415 if (!updateItem.NewData)
417 if (!updateItem.HasStream())
421 const UString name = updateItem.Name;
422 int dotPos = name.ReverseFind(L'.');
425 UString ext = name.Mid(dotPos + 1);
428 exeGroup.Indices.Add(i);
433 generalGroup.Indices.Add(i);
435 if (exeGroup.Indices.Size() > 0)
436 if (!MakeExeMethod(method, maxFilter, exeGroup.Method))
437 exeGroup.Method = method;
438 for (i = 0; i < groups.Size();)
439 if (groups[i].Indices.Size() == 0)
445 static void FromUpdateItemToFileItem(const CUpdateItem &updateItem,
448 file.Name = NItemName::MakeLegalName(updateItem.Name);
449 if (updateItem.AttributesAreDefined)
450 file.SetAttributes(updateItem.Attributes);
452 if (updateItem.IsCreationTimeDefined)
453 file.SetCreationTime(updateItem.CreationTime);
454 if (updateItem.IsLastWriteTimeDefined)
455 file.SetLastWriteTime(updateItem.LastWriteTime);
456 if (updateItem.IsLastAccessTimeDefined)
457 file.SetLastAccessTime(updateItem.LastAccessTime);
459 file.UnPackSize = updateItem.Size;
460 file.IsDirectory = updateItem.IsDirectory;
461 file.IsAnti = updateItem.IsAnti;
462 file.HasStream = updateItem.HasStream();
465 static HRESULT Update2(
466 DECL_EXTERNAL_CODECS_LOC_VARS
468 const CArchiveDatabaseEx *database,
469 const CObjectVector<CUpdateItem> &updateItems,
470 ISequentialOutStream *seqOutStream,
471 IArchiveUpdateCallback *updateCallback,
472 const CUpdateOptions &options)
474 UInt64 numSolidFiles = options.NumSolidFiles;
475 if (numSolidFiles == 0)
478 CMyComPtr<IOutStream> outStream;
479 RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream));
484 UInt64 startBlockSize = database != 0 ? database->ArchiveInfo.StartPosition: 0;
485 if (startBlockSize > 0 && !options.RemoveSfxBlock)
487 RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL));
490 CRecordVector<int> fileIndexToUpdateIndexMap;
493 fileIndexToUpdateIndexMap.Reserve(database->Files.Size());
494 for (int i = 0; i < database->Files.Size(); i++)
495 fileIndexToUpdateIndexMap.Add(-1);
498 for(i = 0; i < updateItems.Size(); i++)
500 int index = updateItems[i].IndexInArchive;
502 fileIndexToUpdateIndexMap[index] = i;
505 CRecordVector<int> folderRefs;
508 for(i = 0; i < database->Folders.Size(); i++)
510 CNum indexInFolder = 0;
511 CNum numCopyItems = 0;
512 CNum numUnPackStreams = database->NumUnPackStreamsVector[i];
513 for (CNum fileIndex = database->FolderStartFileIndex[i];
514 indexInFolder < numUnPackStreams; fileIndex++)
516 if (database->Files[fileIndex].HasStream)
519 int updateIndex = fileIndexToUpdateIndexMap[fileIndex];
520 if (updateIndex >= 0)
521 if (!updateItems[updateIndex].NewData)
525 if (numCopyItems != numUnPackStreams && numCopyItems != 0)
526 return E_NOTIMPL; // It needs repacking !!!
527 if (numCopyItems > 0)
530 folderRefs.Sort(CompareFolderRefs, (void *)database);
533 CArchiveDatabase newDatabase;
535 ////////////////////////////
538 RINOK(archive.Create(seqOutStream, false));
539 RINOK(archive.SkeepPrefixArchiveHeader());
540 UInt64 complexity = 0;
541 for(i = 0; i < folderRefs.Size(); i++)
542 complexity += database->GetFolderFullPackSize(folderRefs[i]);
543 UInt64 inSizeForReduce = 0;
544 for(i = 0; i < updateItems.Size(); i++)
546 const CUpdateItem &updateItem = updateItems[i];
547 if (updateItem.NewData)
549 complexity += updateItem.Size;
550 if (numSolidFiles == 1)
552 if (updateItem.Size > inSizeForReduce)
553 inSizeForReduce = updateItem.Size;
556 inSizeForReduce += updateItem.Size;
559 RINOK(updateCallback->SetTotal(complexity));
561 RINOK(updateCallback->SetCompleted(&complexity));
564 CLocalProgress *lps = new CLocalProgress;
565 CMyComPtr<ICompressProgressInfo> progress = lps;
566 lps->Init(updateCallback, true);
568 /////////////////////////////////////////
571 for(i = 0; i < folderRefs.Size(); i++)
573 int folderIndex = folderRefs[i];
575 lps->ProgressOffset = complexity;
576 UInt64 packSize = database->GetFolderFullPackSize(folderIndex);
577 RINOK(WriteRange(inStream, archive.SeqStream,
578 database->GetFolderStreamPos(folderIndex, 0), packSize, progress));
579 complexity += packSize;
581 const CFolder &folder = database->Folders[folderIndex];
582 CNum startIndex = database->FolderStartPackStreamIndex[folderIndex];
583 for (int j = 0; j < folder.PackStreams.Size(); j++)
585 newDatabase.PackSizes.Add(database->PackSizes[startIndex + j]);
586 // newDatabase.PackCRCsDefined.Add(database.PackCRCsDefined[startIndex + j]);
587 // newDatabase.PackCRCs.Add(database.PackCRCs[startIndex + j]);
589 newDatabase.Folders.Add(folder);
591 CNum numUnPackStreams = database->NumUnPackStreamsVector[folderIndex];
592 newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams);
594 CNum indexInFolder = 0;
595 for (CNum fi = database->FolderStartFileIndex[folderIndex];
596 indexInFolder < numUnPackStreams; fi++)
598 CFileItem file = database->Files[fi];
602 int updateIndex = fileIndexToUpdateIndexMap[fi];
603 if (updateIndex >= 0)
605 const CUpdateItem &updateItem = updateItems[updateIndex];
606 if (updateItem.NewProperties)
609 FromUpdateItemToFileItem(updateItem, file2);
610 file2.UnPackSize = file.UnPackSize;
611 file2.FileCRC = file.FileCRC;
612 file2.IsFileCRCDefined = file.IsFileCRCDefined;
613 file2.HasStream = file.HasStream;
617 newDatabase.Files.Add(file);
622 /////////////////////////////////////////
623 // Compress New Files
625 CObjectVector<CSolidGroup> groups;
626 SplitFilesToGroups(*options.Method, options.UseFilters, options.MaxFilter,
627 updateItems, groups);
629 const UInt32 kMinReduceSize = (1 << 16);
630 if (inSizeForReduce < kMinReduceSize)
631 inSizeForReduce = kMinReduceSize;
633 for (int groupIndex = 0; groupIndex < groups.Size(); groupIndex++)
635 const CSolidGroup &group = groups[groupIndex];
636 int numFiles = group.Indices.Size();
639 CRecordVector<CRefItem> refItems;
640 refItems.Reserve(numFiles);
641 bool sortByType = (numSolidFiles > 1);
642 for (i = 0; i < numFiles; i++)
643 refItems.Add(CRefItem(group.Indices[i], updateItems[group.Indices[i]], sortByType));
644 refItems.Sort(CompareUpdateItems, (void *)&sortByType);
646 CRecordVector<UInt32> indices;
647 indices.Reserve(numFiles);
649 for (i = 0; i < numFiles; i++)
651 UInt32 index = refItems[i].Index;
654 const CUpdateItem &updateItem = updateItems[index];
656 if (updateItem.NewProperties)
657 FromUpdateItemToFileItem(updateItem, file);
659 file = database.Files[updateItem.IndexInArchive];
660 if (file.IsAnti || file.IsDirectory)
662 newDatabase.Files.Add(file);
666 CEncoder encoder(group.Method);
668 for (i = 0; i < numFiles;)
670 UInt64 totalSize = 0;
672 UString prevExtension;
673 for (numSubFiles = 0; i + numSubFiles < numFiles &&
674 numSubFiles < numSolidFiles; numSubFiles++)
676 const CUpdateItem &updateItem = updateItems[indices[i + numSubFiles]];
677 totalSize += updateItem.Size;
678 if (totalSize > options.NumSolidBytes)
680 if (options.SolidExtension)
682 UString ext = updateItem.GetExtension();
683 if (numSubFiles == 0)
686 if (ext.CompareNoCase(prevExtension) != 0)
693 CFolderInStream *inStreamSpec = new CFolderInStream;
694 CMyComPtr<ISequentialInStream> solidInStream(inStreamSpec);
695 inStreamSpec->Init(updateCallback, &indices[i], numSubFiles);
699 int startPackIndex = newDatabase.PackSizes.Size();
700 RINOK(encoder.Encode(
701 EXTERNAL_CODECS_LOC_VARS
702 solidInStream, NULL, &inSizeForReduce, folderItem,
703 archive.SeqStream, newDatabase.PackSizes, progress));
705 for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)
706 lps->OutSize += newDatabase.PackSizes[startPackIndex];
708 lps->InSize += folderItem.GetUnPackSize();
710 // newDatabase.PackCRCsDefined.Add(false);
711 // newDatabase.PackCRCs.Add(0);
713 newDatabase.Folders.Add(folderItem);
715 CNum numUnPackStreams = 0;
716 for (int subIndex = 0; subIndex < numSubFiles; subIndex++)
718 const CUpdateItem &updateItem = updateItems[indices[i + subIndex]];
720 if (updateItem.NewProperties)
721 FromUpdateItemToFileItem(updateItem, file);
723 file = database->Files[updateItem.IndexInArchive];
724 if (file.IsAnti || file.IsDirectory)
728 CFileItem &file = newDatabase.Files[
729 startFileIndexInDatabase + i + subIndex];
731 if (!inStreamSpec->Processed[subIndex])
734 // file.Name += L".locked";
737 file.FileCRC = inStreamSpec->CRCs[subIndex];
738 file.UnPackSize = inStreamSpec->Sizes[subIndex];
739 if (file.UnPackSize != 0)
741 file.IsFileCRCDefined = true;
742 file.HasStream = true;
747 file.IsFileCRCDefined = false;
748 file.HasStream = false;
750 newDatabase.Files.Add(file);
752 // numUnPackStreams = 0 is very bad case for locked files
753 // v3.13 doesn't understand it.
754 newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams);
760 /////////////////////////////////////////
761 // Write Empty Files & Folders
763 CRecordVector<int> emptyRefs;
764 for(i = 0; i < updateItems.Size(); i++)
766 const CUpdateItem &updateItem = updateItems[i];
767 if (updateItem.NewData)
769 if (updateItem.HasStream())
773 if (updateItem.IndexInArchive != -1)
774 if (database->Files[updateItem.IndexInArchive].HasStream)
778 emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems);
779 for(i = 0; i < emptyRefs.Size(); i++)
781 const CUpdateItem &updateItem = updateItems[emptyRefs[i]];
783 if (updateItem.NewProperties)
784 FromUpdateItemToFileItem(updateItem, file);
786 file = database->Files[updateItem.IndexInArchive];
787 newDatabase.Files.Add(file);
792 if (newDatabase.Files.Size() != updateItems.Size())
796 return archive.WriteDatabase(EXTERNAL_CODECS_LOC_VARS
797 newDatabase, options.HeaderMethod, options.HeaderOptions);
802 static const UInt64 k_Copy = 0x0;
804 static HRESULT WriteVolumeHeader(COutArchive &archive, CFileItem &file, const CUpdateOptions &options)
807 coder.NumInStreams = coder.NumOutStreams = 1;
808 coder.MethodID = k_Copy;
811 folder.Coders.Add(coder);
812 folder.PackStreams.Add(0);
814 CNum numUnPackStreams = 0;
815 if (file.UnPackSize != 0)
817 file.IsFileCRCDefined = true;
818 file.HasStream = true;
824 file.IsFileCRCDefined = false;
825 file.HasStream = false;
827 folder.UnPackSizes.Add(file.UnPackSize);
829 CArchiveDatabase newDatabase;
830 newDatabase.Files.Add(file);
831 newDatabase.Folders.Add(folder);
832 newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams);
833 newDatabase.PackSizes.Add(file.UnPackSize);
834 newDatabase.PackCRCsDefined.Add(false);
835 newDatabase.PackCRCs.Add(file.FileCRC);
837 return archive.WriteDatabase(newDatabase,
838 options.HeaderMethod,
843 HRESULT UpdateVolume(
845 const CArchiveDatabaseEx *database,
846 CObjectVector<CUpdateItem> &updateItems,
847 ISequentialOutStream *seqOutStream,
848 IArchiveUpdateCallback *updateCallback,
849 const CUpdateOptions &options)
851 if (updateItems.Size() != 1)
854 CMyComPtr<IArchiveUpdateCallback2> volumeCallback;
855 RINOK(updateCallback->QueryInterface(IID_IArchiveUpdateCallback2, (void **)&volumeCallback));
859 CMyComPtr<ISequentialInStream> fileStream;
860 HRESULT result = updateCallback->GetStream(0, &fileStream);
861 if (result != S_OK && result != S_FALSE)
863 if (result == S_FALSE)
868 const CUpdateItem &updateItem = updateItems[0];
869 if (updateItem.NewProperties)
870 FromUpdateItemToFileItem(updateItem, file);
872 file = database->Files[updateItem.IndexInArchive];
873 if (file.IsAnti || file.IsDirectory)
876 UInt64 complexity = 0;
877 file.IsStartPosDefined = true;
879 for (UInt64 volumeIndex = 0; true; volumeIndex++)
882 RINOK(volumeCallback->GetVolumeSize(volumeIndex, &volSize));
883 UInt64 pureSize = COutArchive::GetVolPureSize(volSize, file.Name.Length(), true);
884 CMyComPtr<ISequentialOutStream> volumeStream;
885 RINOK(volumeCallback->GetVolumeStream(volumeIndex, &volumeStream));
888 RINOK(archive.Create(volumeStream, true));
889 RINOK(archive.SkeepPrefixArchiveHeader());
891 CSequentialInStreamWithCRC *inCrcStreamSpec = new CSequentialInStreamWithCRC;
892 CMyComPtr<ISequentialInStream> inCrcStream = inCrcStreamSpec;
893 inCrcStreamSpec->Init(fileStream);
895 RINOK(WriteRange(inCrcStream, volumeStream, pureSize, updateCallback, complexity));
896 file.UnPackSize = inCrcStreamSpec->GetSize();
897 if (file.UnPackSize == 0)
899 file.FileCRC = inCrcStreamSpec->GetCRC();
900 RINOK(WriteVolumeHeader(archive, file, options));
901 file.StartPos += file.UnPackSize;
902 if (file.UnPackSize < pureSize)
908 class COutVolumeStream:
909 public ISequentialOutStream,
915 CMyComPtr<ISequentialOutStream> _volumeStream;
916 COutArchive _archive;
923 CUpdateOptions _options;
924 CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
925 void Init(IArchiveUpdateCallback2 *volumeCallback,
929 _file.IsStartPosDefined = true;
932 VolumeCallback = volumeCallback;
938 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
941 HRESULT COutVolumeStream::Flush()
945 _file.UnPackSize = _curPos;
946 _file.FileCRC = _crc.GetDigest();
947 RINOK(WriteVolumeHeader(_archive, _file, _options));
949 _volumeStream.Release();
950 _file.StartPos += _file.UnPackSize;
955 STDMETHODIMP COutVolumeStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
957 if(processedSize != NULL)
963 RINOK(VolumeCallback->GetVolumeSize(_volIndex, &_volSize));
964 RINOK(VolumeCallback->GetVolumeStream(_volIndex, &_volumeStream));
967 RINOK(_archive.Create(_volumeStream, true));
968 RINOK(_archive.SkeepPrefixArchiveHeader());
972 UInt64 pureSize = COutArchive::GetVolPureSize(_volSize, _file.Name.Length());
973 UInt32 curSize = (UInt32)MyMin(UInt64(size), pureSize - _curPos);
975 _crc.Update(data, curSize);
976 UInt32 realProcessed;
977 RINOK(_volumeStream->Write(data, curSize, &realProcessed))
978 data = (void *)((Byte *)data + realProcessed);
979 size -= realProcessed;
980 if(processedSize != NULL)
981 *processedSize += realProcessed;
982 _curPos += realProcessed;
983 if (realProcessed != curSize && realProcessed == 0)
985 if (_curPos == pureSize)
996 DECL_EXTERNAL_CODECS_LOC_VARS
998 const CArchiveDatabaseEx *database,
999 const CObjectVector<CUpdateItem> &updateItems,
1000 ISequentialOutStream *seqOutStream,
1001 IArchiveUpdateCallback *updateCallback,
1002 const CUpdateOptions &options)
1008 EXTERNAL_CODECS_LOC_VARS
1009 inStream, database, updateItems,
1010 seqOutStream, updateCallback, options);
1012 if (options.VolumeMode)
1013 return UpdateVolume(inStream, database, updateItems,
1014 seqOutStream, updateCallback, options);
1015 COutVolumeStream *volStreamSpec = new COutVolumeStream;
1016 CMyComPtr<ISequentialOutStream> volStream = volStreamSpec;
1017 CMyComPtr<IArchiveUpdateCallback2> volumeCallback;
1018 RINOK(updateCallback->QueryInterface(IID_IArchiveUpdateCallback2, (void **)&volumeCallback));
1019 if (!volumeCallback)
1021 volStreamSpec->Init(volumeCallback, L"a.7z");
1022 volStreamSpec->_options = options;
1023 RINOK(Update2(inStream, database, updateItems,
1024 volStream, updateCallback, options));
1025 return volStreamSpec->Flush();