7zUpdate.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029
  1. // UpdateMain.cpp
  2. #include "StdAfx.h"
  3. #include "7zUpdate.h"
  4. #include "7zFolderInStream.h"
  5. #include "7zEncode.h"
  6. #include "7zHandler.h"
  7. #include "7zOut.h"
  8. #include "../../Compress/Copy/CopyCoder.h"
  9. #include "../../Common/ProgressUtils.h"
  10. #include "../../Common/LimitedStreams.h"
  11. #include "../../Common/LimitedStreams.h"
  12. #include "../Common/ItemNameUtils.h"
  13. namespace NArchive {
  14. namespace N7z {
  15. static const wchar_t *kMatchFinderForBCJ2_LZMA = L"BT2";
  16. static const UInt32 kDictionaryForBCJ2_LZMA = 1 << 20;
  17. static const UInt32 kAlgorithmForBCJ2_LZMA = 1;
  18. static const UInt32 kNumFastBytesForBCJ2_LZMA = 64;
  19. static HRESULT WriteRange(IInStream *inStream, ISequentialOutStream *outStream,
  20. UInt64 position, UInt64 size, ICompressProgressInfo *progress)
  21. {
  22. RINOK(inStream->Seek(position, STREAM_SEEK_SET, 0));
  23. CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
  24. CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec);
  25. streamSpec->SetStream(inStream);
  26. streamSpec->Init(size);
  27. NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
  28. CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
  29. RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress));
  30. return (copyCoderSpec->TotalSize == size ? S_OK : E_FAIL);
  31. }
  32. static int GetReverseSlashPos(const UString &name)
  33. {
  34. int slashPos = name.ReverseFind(L'/');
  35. #ifdef _WIN32
  36. int slash1Pos = name.ReverseFind(L'\\');
  37. slashPos = MyMax(slashPos, slash1Pos);
  38. #endif
  39. return slashPos;
  40. }
  41. int CUpdateItem::GetExtensionPos() const
  42. {
  43. int slashPos = GetReverseSlashPos(Name);
  44. int dotPos = Name.ReverseFind(L'.');
  45. if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0))
  46. return Name.Length();
  47. return dotPos + 1;
  48. }
  49. UString CUpdateItem::GetExtension() const
  50. {
  51. return Name.Mid(GetExtensionPos());
  52. }
  53. #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
  54. static int CompareBuffers(const CByteBuffer &a1, const CByteBuffer &a2)
  55. {
  56. size_t c1 = a1.GetCapacity();
  57. size_t c2 = a2.GetCapacity();
  58. RINOZ(MyCompare(c1, c2));
  59. for (size_t i = 0; i < c1; i++)
  60. RINOZ(MyCompare(a1[i], a2[i]));
  61. return 0;
  62. }
  63. static int CompareCoders(const CCoderInfo &c1, const CCoderInfo &c2)
  64. {
  65. RINOZ(MyCompare(c1.NumInStreams, c2.NumInStreams));
  66. RINOZ(MyCompare(c1.NumOutStreams, c2.NumOutStreams));
  67. RINOZ(MyCompare(c1.MethodID, c2.MethodID));
  68. return CompareBuffers(c1.Properties, c2.Properties);
  69. }
  70. static int CompareBindPairs(const CBindPair &b1, const CBindPair &b2)
  71. {
  72. RINOZ(MyCompare(b1.InIndex, b2.InIndex));
  73. return MyCompare(b1.OutIndex, b2.OutIndex);
  74. }
  75. static int CompareFolders(const CFolder &f1, const CFolder &f2)
  76. {
  77. int s1 = f1.Coders.Size();
  78. int s2 = f2.Coders.Size();
  79. RINOZ(MyCompare(s1, s2));
  80. int i;
  81. for (i = 0; i < s1; i++)
  82. RINOZ(CompareCoders(f1.Coders[i], f2.Coders[i]));
  83. s1 = f1.BindPairs.Size();
  84. s2 = f2.BindPairs.Size();
  85. RINOZ(MyCompare(s1, s2));
  86. for (i = 0; i < s1; i++)
  87. RINOZ(CompareBindPairs(f1.BindPairs[i], f2.BindPairs[i]));
  88. return 0;
  89. }
  90. static int CompareFiles(const CFileItem &f1, const CFileItem &f2)
  91. {
  92. return MyStringCompareNoCase(f1.Name, f2.Name);
  93. }
  94. static int CompareFolderRefs(const int *p1, const int *p2, void *param)
  95. {
  96. int i1 = *p1;
  97. int i2 = *p2;
  98. const CArchiveDatabaseEx &db = *(const CArchiveDatabaseEx *)param;
  99. RINOZ(CompareFolders(
  100. db.Folders[i1],
  101. db.Folders[i2]));
  102. RINOZ(MyCompare(
  103. db.NumUnPackStreamsVector[i1],
  104. db.NumUnPackStreamsVector[i2]));
  105. if (db.NumUnPackStreamsVector[i1] == 0)
  106. return 0;
  107. return CompareFiles(
  108. db.Files[db.FolderStartFileIndex[i1]],
  109. db.Files[db.FolderStartFileIndex[i2]]);
  110. }
  111. ////////////////////////////////////////////////////////////
  112. static int CompareEmptyItems(const int *p1, const int *p2, void *param)
  113. {
  114. const CObjectVector<CUpdateItem> &updateItems = *(const CObjectVector<CUpdateItem> *)param;
  115. const CUpdateItem &u1 = updateItems[*p1];
  116. const CUpdateItem &u2 = updateItems[*p2];
  117. if (u1.IsDirectory != u2.IsDirectory)
  118. return (u1.IsDirectory) ? 1 : -1;
  119. if (u1.IsDirectory)
  120. {
  121. if (u1.IsAnti != u2.IsAnti)
  122. return (u1.IsAnti ? 1 : -1);
  123. int n = MyStringCompareNoCase(u1.Name, u2.Name);
  124. return -n;
  125. }
  126. if (u1.IsAnti != u2.IsAnti)
  127. return (u1.IsAnti ? 1 : -1);
  128. return MyStringCompareNoCase(u1.Name, u2.Name);
  129. }
  130. static const char *g_Exts =
  131. " lzma 7z ace arc arj bz bz2 deb lzo lzx gz pak rpm sit tgz tbz tbz2 tgz cab ha lha lzh rar zoo"
  132. " zip jar ear war msi"
  133. " 3gp avi mov mpeg mpg mpe wmv"
  134. " aac ape fla flac la mp3 m4a mp4 ofr ogg pac ra rm rka shn swa tta wv wma wav"
  135. " swf "
  136. " chm hxi hxs"
  137. " gif jpeg jpg jp2 png tiff bmp ico psd psp"
  138. " awg ps eps cgm dxf svg vrml wmf emf ai md"
  139. " cad dwg pps key sxi"
  140. " max 3ds"
  141. " iso bin nrg mdf img pdi tar cpio xpi"
  142. " vfd vhd vud vmc vsv"
  143. " vmdk dsk nvram vmem vmsd vmsn vmss vmtm"
  144. " inl inc idl acf asa h hpp hxx c cpp cxx rc java cs pas bas vb cls ctl frm dlg def"
  145. " f77 f f90 f95"
  146. " asm sql manifest dep "
  147. " mak clw csproj vcproj sln dsp dsw "
  148. " class "
  149. " bat cmd"
  150. " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml"
  151. " awk sed hta js php php3 php4 php5 phptml pl pm py pyo rb sh tcl vbs"
  152. " text txt tex ans asc srt reg ini doc docx mcw dot rtf hlp xls xlr xlt xlw ppt pdf"
  153. " sxc sxd sxi sxg sxw stc sti stw stm odt ott odg otg odp otp ods ots odf"
  154. " abw afp cwk lwp wpd wps wpt wrf wri"
  155. " abf afm bdf fon mgf otf pcf pfa snf ttf"
  156. " dbf mdb nsf ntf wdb db fdb gdb"
  157. " exe dll ocx vbx sfx sys tlb awx com obj lib out o so "
  158. " pdb pch idb ncb opt";
  159. int GetExtIndex(const char *ext)
  160. {
  161. int extIndex = 1;
  162. const char *p = g_Exts;
  163. for (;;)
  164. {
  165. char c = *p++;
  166. if (c == 0)
  167. return extIndex;
  168. if (c == ' ')
  169. continue;
  170. int pos = 0;
  171. for (;;)
  172. {
  173. char c2 = ext[pos++];
  174. if (c2 == 0 && (c == 0 || c == ' '))
  175. return extIndex;
  176. if (c != c2)
  177. break;
  178. c = *p++;
  179. }
  180. extIndex++;
  181. for (;;)
  182. {
  183. if (c == 0)
  184. return extIndex;
  185. if (c == ' ')
  186. break;
  187. c = *p++;
  188. }
  189. }
  190. }
  191. struct CRefItem
  192. {
  193. UInt32 Index;
  194. const CUpdateItem *UpdateItem;
  195. UInt32 ExtensionPos;
  196. UInt32 NamePos;
  197. int ExtensionIndex;
  198. CRefItem(UInt32 index, const CUpdateItem &updateItem, bool sortByType):
  199. Index(index),
  200. UpdateItem(&updateItem),
  201. ExtensionPos(0),
  202. NamePos(0),
  203. ExtensionIndex(0)
  204. {
  205. if (sortByType)
  206. {
  207. int slashPos = GetReverseSlashPos(updateItem.Name);
  208. NamePos = ((slashPos >= 0) ? (slashPos + 1) : 0);
  209. int dotPos = updateItem.Name.ReverseFind(L'.');
  210. if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0))
  211. ExtensionPos = updateItem.Name.Length();
  212. else
  213. {
  214. ExtensionPos = dotPos + 1;
  215. UString us = updateItem.Name.Mid(ExtensionPos);
  216. if (!us.IsEmpty())
  217. {
  218. us.MakeLower();
  219. int i;
  220. AString s;
  221. for (i = 0; i < us.Length(); i++)
  222. {
  223. wchar_t c = us[i];
  224. if (c >= 0x80)
  225. break;
  226. s += (char)c;
  227. }
  228. if (i == us.Length())
  229. ExtensionIndex = GetExtIndex(s);
  230. else
  231. ExtensionIndex = 0;
  232. }
  233. }
  234. }
  235. }
  236. };
  237. static int CompareUpdateItems(const CRefItem *p1, const CRefItem *p2, void *param)
  238. {
  239. const CRefItem &a1 = *p1;
  240. const CRefItem &a2 = *p2;
  241. const CUpdateItem &u1 = *a1.UpdateItem;
  242. const CUpdateItem &u2 = *a2.UpdateItem;
  243. int n;
  244. if (u1.IsDirectory != u2.IsDirectory)
  245. return (u1.IsDirectory) ? 1 : -1;
  246. if (u1.IsDirectory)
  247. {
  248. if (u1.IsAnti != u2.IsAnti)
  249. return (u1.IsAnti ? 1 : -1);
  250. n = MyStringCompareNoCase(u1.Name, u2.Name);
  251. return -n;
  252. }
  253. bool sortByType = *(bool *)param;
  254. if (sortByType)
  255. {
  256. RINOZ(MyCompare(a1.ExtensionIndex, a2.ExtensionIndex))
  257. RINOZ(MyStringCompareNoCase(u1.Name + a1.ExtensionPos, u2.Name + a2.ExtensionPos));
  258. RINOZ(MyStringCompareNoCase(u1.Name + a1.NamePos, u2.Name + a2.NamePos));
  259. if (u1.IsLastWriteTimeDefined && u2.IsLastWriteTimeDefined)
  260. RINOZ(CompareFileTime(&u1.LastWriteTime, &u2.LastWriteTime));
  261. RINOZ(MyCompare(u1.Size, u2.Size))
  262. }
  263. return MyStringCompareNoCase(u1.Name, u2.Name);
  264. }
  265. struct CSolidGroup
  266. {
  267. CCompressionMethodMode Method;
  268. CRecordVector<UInt32> Indices;
  269. };
  270. static wchar_t *g_ExeExts[] =
  271. {
  272. L"dll",
  273. L"exe",
  274. L"ocx",
  275. L"sfx",
  276. L"sys"
  277. };
  278. static bool IsExeFile(const UString &ext)
  279. {
  280. for (int i = 0; i < sizeof(g_ExeExts) / sizeof(g_ExeExts[0]); i++)
  281. if (ext.CompareNoCase(g_ExeExts[i]) == 0)
  282. return true;
  283. return false;
  284. }
  285. static const UInt64 k_LZMA = 0x030101;
  286. static const UInt64 k_BCJ = 0x03030103;
  287. static const UInt64 k_BCJ2 = 0x0303011B;
  288. static bool GetMethodFull(UInt64 methodID,
  289. UInt32 numInStreams, CMethodFull &methodResult)
  290. {
  291. methodResult.Id = methodID;
  292. methodResult.NumInStreams = numInStreams;
  293. methodResult.NumOutStreams = 1;
  294. return true;
  295. }
  296. static bool MakeExeMethod(const CCompressionMethodMode &method,
  297. bool bcj2Filter, CCompressionMethodMode &exeMethod)
  298. {
  299. exeMethod = method;
  300. if (bcj2Filter)
  301. {
  302. CMethodFull methodFull;
  303. if (!GetMethodFull(k_BCJ2, 4, methodFull))
  304. return false;
  305. exeMethod.Methods.Insert(0, methodFull);
  306. if (!GetMethodFull(k_LZMA, 1, methodFull))
  307. return false;
  308. {
  309. CProp property;
  310. property.Id = NCoderPropID::kAlgorithm;
  311. property.Value = kAlgorithmForBCJ2_LZMA;
  312. methodFull.Properties.Add(property);
  313. }
  314. {
  315. CProp property;
  316. property.Id = NCoderPropID::kMatchFinder;
  317. property.Value = kMatchFinderForBCJ2_LZMA;
  318. methodFull.Properties.Add(property);
  319. }
  320. {
  321. CProp property;
  322. property.Id = NCoderPropID::kDictionarySize;
  323. property.Value = kDictionaryForBCJ2_LZMA;
  324. methodFull.Properties.Add(property);
  325. }
  326. {
  327. CProp property;
  328. property.Id = NCoderPropID::kNumFastBytes;
  329. property.Value = kNumFastBytesForBCJ2_LZMA;
  330. methodFull.Properties.Add(property);
  331. }
  332. exeMethod.Methods.Add(methodFull);
  333. exeMethod.Methods.Add(methodFull);
  334. CBind bind;
  335. bind.OutCoder = 0;
  336. bind.InStream = 0;
  337. bind.InCoder = 1;
  338. bind.OutStream = 0;
  339. exeMethod.Binds.Add(bind);
  340. bind.InCoder = 2;
  341. bind.OutStream = 1;
  342. exeMethod.Binds.Add(bind);
  343. bind.InCoder = 3;
  344. bind.OutStream = 2;
  345. exeMethod.Binds.Add(bind);
  346. }
  347. else
  348. {
  349. CMethodFull methodFull;
  350. if (!GetMethodFull(k_BCJ, 1, methodFull))
  351. return false;
  352. exeMethod.Methods.Insert(0, methodFull);
  353. CBind bind;
  354. bind.OutCoder = 0;
  355. bind.InStream = 0;
  356. bind.InCoder = 1;
  357. bind.OutStream = 0;
  358. exeMethod.Binds.Add(bind);
  359. }
  360. return true;
  361. }
  362. static void SplitFilesToGroups(
  363. const CCompressionMethodMode &method,
  364. bool useFilters, bool maxFilter,
  365. const CObjectVector<CUpdateItem> &updateItems,
  366. CObjectVector<CSolidGroup> &groups)
  367. {
  368. if (method.Methods.Size() != 1 || method.Binds.Size() != 0)
  369. useFilters = false;
  370. groups.Clear();
  371. groups.Add(CSolidGroup());
  372. groups.Add(CSolidGroup());
  373. CSolidGroup &generalGroup = groups[0];
  374. CSolidGroup &exeGroup = groups[1];
  375. generalGroup.Method = method;
  376. int i;
  377. for (i = 0; i < updateItems.Size(); i++)
  378. {
  379. const CUpdateItem &updateItem = updateItems[i];
  380. if (!updateItem.NewData)
  381. continue;
  382. if (!updateItem.HasStream())
  383. continue;
  384. if (useFilters)
  385. {
  386. const UString name = updateItem.Name;
  387. int dotPos = name.ReverseFind(L'.');
  388. if (dotPos >= 0)
  389. {
  390. UString ext = name.Mid(dotPos + 1);
  391. if (IsExeFile(ext))
  392. {
  393. exeGroup.Indices.Add(i);
  394. continue;
  395. }
  396. }
  397. }
  398. generalGroup.Indices.Add(i);
  399. }
  400. if (exeGroup.Indices.Size() > 0)
  401. if (!MakeExeMethod(method, maxFilter, exeGroup.Method))
  402. exeGroup.Method = method;
  403. for (i = 0; i < groups.Size();)
  404. if (groups[i].Indices.Size() == 0)
  405. groups.Delete(i);
  406. else
  407. i++;
  408. }
  409. static void FromUpdateItemToFileItem(const CUpdateItem &updateItem,
  410. CFileItem &file)
  411. {
  412. file.Name = NItemName::MakeLegalName(updateItem.Name);
  413. if (updateItem.AttributesAreDefined)
  414. file.SetAttributes(updateItem.Attributes);
  415. if (updateItem.IsCreationTimeDefined)
  416. file.SetCreationTime(updateItem.CreationTime);
  417. if (updateItem.IsLastWriteTimeDefined)
  418. file.SetLastWriteTime(updateItem.LastWriteTime);
  419. if (updateItem.IsLastAccessTimeDefined)
  420. file.SetLastAccessTime(updateItem.LastAccessTime);
  421. file.UnPackSize = updateItem.Size;
  422. file.IsDirectory = updateItem.IsDirectory;
  423. file.IsAnti = updateItem.IsAnti;
  424. file.HasStream = updateItem.HasStream();
  425. }
  426. static HRESULT Update2(
  427. DECL_EXTERNAL_CODECS_LOC_VARS
  428. IInStream *inStream,
  429. const CArchiveDatabaseEx *database,
  430. const CObjectVector<CUpdateItem> &updateItems,
  431. ISequentialOutStream *seqOutStream,
  432. IArchiveUpdateCallback *updateCallback,
  433. const CUpdateOptions &options)
  434. {
  435. UInt64 numSolidFiles = options.NumSolidFiles;
  436. if (numSolidFiles == 0)
  437. numSolidFiles = 1;
  438. /*
  439. CMyComPtr<IOutStream> outStream;
  440. RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream));
  441. if (!outStream)
  442. return E_NOTIMPL;
  443. */
  444. UInt64 startBlockSize = database != 0 ? database->ArchiveInfo.StartPosition: 0;
  445. if (startBlockSize > 0 && !options.RemoveSfxBlock)
  446. {
  447. RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL));
  448. }
  449. CRecordVector<int> fileIndexToUpdateIndexMap;
  450. if (database != 0)
  451. {
  452. fileIndexToUpdateIndexMap.Reserve(database->Files.Size());
  453. for (int i = 0; i < database->Files.Size(); i++)
  454. fileIndexToUpdateIndexMap.Add(-1);
  455. }
  456. int i;
  457. for(i = 0; i < updateItems.Size(); i++)
  458. {
  459. int index = updateItems[i].IndexInArchive;
  460. if (index != -1)
  461. fileIndexToUpdateIndexMap[index] = i;
  462. }
  463. CRecordVector<int> folderRefs;
  464. if (database != 0)
  465. {
  466. for(i = 0; i < database->Folders.Size(); i++)
  467. {
  468. CNum indexInFolder = 0;
  469. CNum numCopyItems = 0;
  470. CNum numUnPackStreams = database->NumUnPackStreamsVector[i];
  471. for (CNum fileIndex = database->FolderStartFileIndex[i];
  472. indexInFolder < numUnPackStreams; fileIndex++)
  473. {
  474. if (database->Files[fileIndex].HasStream)
  475. {
  476. indexInFolder++;
  477. int updateIndex = fileIndexToUpdateIndexMap[fileIndex];
  478. if (updateIndex >= 0)
  479. if (!updateItems[updateIndex].NewData)
  480. numCopyItems++;
  481. }
  482. }
  483. if (numCopyItems != numUnPackStreams && numCopyItems != 0)
  484. return E_NOTIMPL; // It needs repacking !!!
  485. if (numCopyItems > 0)
  486. folderRefs.Add(i);
  487. }
  488. folderRefs.Sort(CompareFolderRefs, (void *)database);
  489. }
  490. CArchiveDatabase newDatabase;
  491. ////////////////////////////
  492. COutArchive archive;
  493. RINOK(archive.Create(seqOutStream, false));
  494. RINOK(archive.SkeepPrefixArchiveHeader());
  495. UInt64 complexity = 0;
  496. for(i = 0; i < folderRefs.Size(); i++)
  497. complexity += database->GetFolderFullPackSize(folderRefs[i]);
  498. UInt64 inSizeForReduce = 0;
  499. for(i = 0; i < updateItems.Size(); i++)
  500. {
  501. const CUpdateItem &updateItem = updateItems[i];
  502. if (updateItem.NewData)
  503. {
  504. complexity += updateItem.Size;
  505. if (numSolidFiles == 1)
  506. {
  507. if (updateItem.Size > inSizeForReduce)
  508. inSizeForReduce = updateItem.Size;
  509. }
  510. else
  511. inSizeForReduce += updateItem.Size;
  512. }
  513. }
  514. RINOK(updateCallback->SetTotal(complexity));
  515. complexity = 0;
  516. RINOK(updateCallback->SetCompleted(&complexity));
  517. CLocalProgress *lps = new CLocalProgress;
  518. CMyComPtr<ICompressProgressInfo> progress = lps;
  519. lps->Init(updateCallback, true);
  520. /////////////////////////////////////////
  521. // Write Copy Items
  522. for(i = 0; i < folderRefs.Size(); i++)
  523. {
  524. int folderIndex = folderRefs[i];
  525. lps->ProgressOffset = complexity;
  526. UInt64 packSize = database->GetFolderFullPackSize(folderIndex);
  527. RINOK(WriteRange(inStream, archive.SeqStream,
  528. database->GetFolderStreamPos(folderIndex, 0), packSize, progress));
  529. complexity += packSize;
  530. const CFolder &folder = database->Folders[folderIndex];
  531. CNum startIndex = database->FolderStartPackStreamIndex[folderIndex];
  532. for (int j = 0; j < folder.PackStreams.Size(); j++)
  533. {
  534. newDatabase.PackSizes.Add(database->PackSizes[startIndex + j]);
  535. // newDatabase.PackCRCsDefined.Add(database.PackCRCsDefined[startIndex + j]);
  536. // newDatabase.PackCRCs.Add(database.PackCRCs[startIndex + j]);
  537. }
  538. newDatabase.Folders.Add(folder);
  539. CNum numUnPackStreams = database->NumUnPackStreamsVector[folderIndex];
  540. newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams);
  541. CNum indexInFolder = 0;
  542. for (CNum fi = database->FolderStartFileIndex[folderIndex];
  543. indexInFolder < numUnPackStreams; fi++)
  544. {
  545. CFileItem file = database->Files[fi];
  546. if (file.HasStream)
  547. {
  548. indexInFolder++;
  549. int updateIndex = fileIndexToUpdateIndexMap[fi];
  550. if (updateIndex >= 0)
  551. {
  552. const CUpdateItem &updateItem = updateItems[updateIndex];
  553. if (updateItem.NewProperties)
  554. {
  555. CFileItem file2;
  556. FromUpdateItemToFileItem(updateItem, file2);
  557. file2.UnPackSize = file.UnPackSize;
  558. file2.FileCRC = file.FileCRC;
  559. file2.IsFileCRCDefined = file.IsFileCRCDefined;
  560. file2.HasStream = file.HasStream;
  561. file = file2;
  562. }
  563. }
  564. newDatabase.Files.Add(file);
  565. }
  566. }
  567. }
  568. /////////////////////////////////////////
  569. // Compress New Files
  570. CObjectVector<CSolidGroup> groups;
  571. SplitFilesToGroups(*options.Method, options.UseFilters, options.MaxFilter,
  572. updateItems, groups);
  573. const UInt32 kMinReduceSize = (1 << 16);
  574. if (inSizeForReduce < kMinReduceSize)
  575. inSizeForReduce = kMinReduceSize;
  576. for (int groupIndex = 0; groupIndex < groups.Size(); groupIndex++)
  577. {
  578. const CSolidGroup &group = groups[groupIndex];
  579. int numFiles = group.Indices.Size();
  580. if (numFiles == 0)
  581. continue;
  582. CRecordVector<CRefItem> refItems;
  583. refItems.Reserve(numFiles);
  584. bool sortByType = (numSolidFiles > 1);
  585. for (i = 0; i < numFiles; i++)
  586. refItems.Add(CRefItem(group.Indices[i], updateItems[group.Indices[i]], sortByType));
  587. refItems.Sort(CompareUpdateItems, (void *)&sortByType);
  588. CRecordVector<UInt32> indices;
  589. indices.Reserve(numFiles);
  590. for (i = 0; i < numFiles; i++)
  591. {
  592. UInt32 index = refItems[i].Index;
  593. indices.Add(index);
  594. /*
  595. const CUpdateItem &updateItem = updateItems[index];
  596. CFileItem file;
  597. if (updateItem.NewProperties)
  598. FromUpdateItemToFileItem(updateItem, file);
  599. else
  600. file = database.Files[updateItem.IndexInArchive];
  601. if (file.IsAnti || file.IsDirectory)
  602. return E_FAIL;
  603. newDatabase.Files.Add(file);
  604. */
  605. }
  606. CEncoder encoder(group.Method);
  607. for (i = 0; i < numFiles;)
  608. {
  609. UInt64 totalSize = 0;
  610. int numSubFiles;
  611. UString prevExtension;
  612. for (numSubFiles = 0; i + numSubFiles < numFiles &&
  613. numSubFiles < numSolidFiles; numSubFiles++)
  614. {
  615. const CUpdateItem &updateItem = updateItems[indices[i + numSubFiles]];
  616. totalSize += updateItem.Size;
  617. if (totalSize > options.NumSolidBytes)
  618. break;
  619. if (options.SolidExtension)
  620. {
  621. UString ext = updateItem.GetExtension();
  622. if (numSubFiles == 0)
  623. prevExtension = ext;
  624. else
  625. if (ext.CompareNoCase(prevExtension) != 0)
  626. break;
  627. }
  628. }
  629. if (numSubFiles < 1)
  630. numSubFiles = 1;
  631. CFolderInStream *inStreamSpec = new CFolderInStream;
  632. CMyComPtr<ISequentialInStream> solidInStream(inStreamSpec);
  633. inStreamSpec->Init(updateCallback, &indices[i], numSubFiles);
  634. CFolder folderItem;
  635. int startPackIndex = newDatabase.PackSizes.Size();
  636. RINOK(encoder.Encode(
  637. EXTERNAL_CODECS_LOC_VARS
  638. solidInStream, NULL, &inSizeForReduce, folderItem,
  639. archive.SeqStream, newDatabase.PackSizes, progress));
  640. for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)
  641. lps->OutSize += newDatabase.PackSizes[startPackIndex];
  642. lps->InSize += folderItem.GetUnPackSize();
  643. // for()
  644. // newDatabase.PackCRCsDefined.Add(false);
  645. // newDatabase.PackCRCs.Add(0);
  646. newDatabase.Folders.Add(folderItem);
  647. CNum numUnPackStreams = 0;
  648. for (int subIndex = 0; subIndex < numSubFiles; subIndex++)
  649. {
  650. const CUpdateItem &updateItem = updateItems[indices[i + subIndex]];
  651. CFileItem file;
  652. if (updateItem.NewProperties)
  653. FromUpdateItemToFileItem(updateItem, file);
  654. else
  655. file = database->Files[updateItem.IndexInArchive];
  656. if (file.IsAnti || file.IsDirectory)
  657. return E_FAIL;
  658. /*
  659. CFileItem &file = newDatabase.Files[
  660. startFileIndexInDatabase + i + subIndex];
  661. */
  662. if (!inStreamSpec->Processed[subIndex])
  663. {
  664. continue;
  665. // file.Name += L".locked";
  666. }
  667. file.FileCRC = inStreamSpec->CRCs[subIndex];
  668. file.UnPackSize = inStreamSpec->Sizes[subIndex];
  669. if (file.UnPackSize != 0)
  670. {
  671. file.IsFileCRCDefined = true;
  672. file.HasStream = true;
  673. numUnPackStreams++;
  674. }
  675. else
  676. {
  677. file.IsFileCRCDefined = false;
  678. file.HasStream = false;
  679. }
  680. newDatabase.Files.Add(file);
  681. }
  682. // numUnPackStreams = 0 is very bad case for locked files
  683. // v3.13 doesn't understand it.
  684. newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams);
  685. i += numSubFiles;
  686. }
  687. }
  688. {
  689. /////////////////////////////////////////
  690. // Write Empty Files & Folders
  691. CRecordVector<int> emptyRefs;
  692. for(i = 0; i < updateItems.Size(); i++)
  693. {
  694. const CUpdateItem &updateItem = updateItems[i];
  695. if (updateItem.NewData)
  696. {
  697. if (updateItem.HasStream())
  698. continue;
  699. }
  700. else
  701. if (updateItem.IndexInArchive != -1)
  702. if (database->Files[updateItem.IndexInArchive].HasStream)
  703. continue;
  704. emptyRefs.Add(i);
  705. }
  706. emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems);
  707. for(i = 0; i < emptyRefs.Size(); i++)
  708. {
  709. const CUpdateItem &updateItem = updateItems[emptyRefs[i]];
  710. CFileItem file;
  711. if (updateItem.NewProperties)
  712. FromUpdateItemToFileItem(updateItem, file);
  713. else
  714. file = database->Files[updateItem.IndexInArchive];
  715. newDatabase.Files.Add(file);
  716. }
  717. }
  718. /*
  719. if (newDatabase.Files.Size() != updateItems.Size())
  720. return E_FAIL;
  721. */
  722. return archive.WriteDatabase(EXTERNAL_CODECS_LOC_VARS
  723. newDatabase, options.HeaderMethod, options.HeaderOptions);
  724. }
  725. #ifdef _7Z_VOL
  726. static const UInt64 k_Copy = 0x0;
  727. static HRESULT WriteVolumeHeader(COutArchive &archive, CFileItem &file, const CUpdateOptions &options)
  728. {
  729. CCoderInfo coder;
  730. coder.NumInStreams = coder.NumOutStreams = 1;
  731. coder.MethodID = k_Copy;
  732. CFolder folder;
  733. folder.Coders.Add(coder);
  734. folder.PackStreams.Add(0);
  735. CNum numUnPackStreams = 0;
  736. if (file.UnPackSize != 0)
  737. {
  738. file.IsFileCRCDefined = true;
  739. file.HasStream = true;
  740. numUnPackStreams++;
  741. }
  742. else
  743. {
  744. throw 1;
  745. file.IsFileCRCDefined = false;
  746. file.HasStream = false;
  747. }
  748. folder.UnPackSizes.Add(file.UnPackSize);
  749. CArchiveDatabase newDatabase;
  750. newDatabase.Files.Add(file);
  751. newDatabase.Folders.Add(folder);
  752. newDatabase.NumUnPackStreamsVector.Add(numUnPackStreams);
  753. newDatabase.PackSizes.Add(file.UnPackSize);
  754. newDatabase.PackCRCsDefined.Add(false);
  755. newDatabase.PackCRCs.Add(file.FileCRC);
  756. return archive.WriteDatabase(newDatabase,
  757. options.HeaderMethod,
  758. false,
  759. false);
  760. }
  761. HRESULT UpdateVolume(
  762. IInStream *inStream,
  763. const CArchiveDatabaseEx *database,
  764. CObjectVector<CUpdateItem> &updateItems,
  765. ISequentialOutStream *seqOutStream,
  766. IArchiveUpdateCallback *updateCallback,
  767. const CUpdateOptions &options)
  768. {
  769. if (updateItems.Size() != 1)
  770. return E_NOTIMPL;
  771. CMyComPtr<IArchiveUpdateCallback2> volumeCallback;
  772. RINOK(updateCallback->QueryInterface(IID_IArchiveUpdateCallback2, (void **)&volumeCallback));
  773. if (!volumeCallback)
  774. return E_NOTIMPL;
  775. CMyComPtr<ISequentialInStream> fileStream;
  776. HRESULT result = updateCallback->GetStream(0, &fileStream);
  777. if (result != S_OK && result != S_FALSE)
  778. return result;
  779. if (result == S_FALSE)
  780. return E_FAIL;
  781. CFileItem file;
  782. const CUpdateItem &updateItem = updateItems[0];
  783. if (updateItem.NewProperties)
  784. FromUpdateItemToFileItem(updateItem, file);
  785. else
  786. file = database->Files[updateItem.IndexInArchive];
  787. if (file.IsAnti || file.IsDirectory)
  788. return E_FAIL;
  789. UInt64 complexity = 0;
  790. file.IsStartPosDefined = true;
  791. file.StartPos = 0;
  792. for (UInt64 volumeIndex = 0; true; volumeIndex++)
  793. {
  794. UInt64 volSize;
  795. RINOK(volumeCallback->GetVolumeSize(volumeIndex, &volSize));
  796. UInt64 pureSize = COutArchive::GetVolPureSize(volSize, file.Name.Length(), true);
  797. CMyComPtr<ISequentialOutStream> volumeStream;
  798. RINOK(volumeCallback->GetVolumeStream(volumeIndex, &volumeStream));
  799. COutArchive archive;
  800. RINOK(archive.Create(volumeStream, true));
  801. RINOK(archive.SkeepPrefixArchiveHeader());
  802. CSequentialInStreamWithCRC *inCrcStreamSpec = new CSequentialInStreamWithCRC;
  803. CMyComPtr<ISequentialInStream> inCrcStream = inCrcStreamSpec;
  804. inCrcStreamSpec->Init(fileStream);
  805. RINOK(WriteRange(inCrcStream, volumeStream, pureSize, updateCallback, complexity));
  806. file.UnPackSize = inCrcStreamSpec->GetSize();
  807. if (file.UnPackSize == 0)
  808. break;
  809. file.FileCRC = inCrcStreamSpec->GetCRC();
  810. RINOK(WriteVolumeHeader(archive, file, options));
  811. file.StartPos += file.UnPackSize;
  812. if (file.UnPackSize < pureSize)
  813. break;
  814. }
  815. return S_OK;
  816. }
  817. class COutVolumeStream:
  818. public ISequentialOutStream,
  819. public CMyUnknownImp
  820. {
  821. int _volIndex;
  822. UInt64 _volSize;
  823. UInt64 _curPos;
  824. CMyComPtr<ISequentialOutStream> _volumeStream;
  825. COutArchive _archive;
  826. CCRC _crc;
  827. public:
  828. MY_UNKNOWN_IMP
  829. CFileItem _file;
  830. CUpdateOptions _options;
  831. CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
  832. void Init(IArchiveUpdateCallback2 *volumeCallback,
  833. const UString &name)
  834. {
  835. _file.Name = name;
  836. _file.IsStartPosDefined = true;
  837. _file.StartPos = 0;
  838. VolumeCallback = volumeCallback;
  839. _volIndex = 0;
  840. _volSize = 0;
  841. }
  842. HRESULT Flush();
  843. STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
  844. };
  845. HRESULT COutVolumeStream::Flush()
  846. {
  847. if (_volumeStream)
  848. {
  849. _file.UnPackSize = _curPos;
  850. _file.FileCRC = _crc.GetDigest();
  851. RINOK(WriteVolumeHeader(_archive, _file, _options));
  852. _archive.Close();
  853. _volumeStream.Release();
  854. _file.StartPos += _file.UnPackSize;
  855. }
  856. return S_OK;
  857. }
  858. STDMETHODIMP COutVolumeStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
  859. {
  860. if(processedSize != NULL)
  861. *processedSize = 0;
  862. while(size > 0)
  863. {
  864. if (!_volumeStream)
  865. {
  866. RINOK(VolumeCallback->GetVolumeSize(_volIndex, &_volSize));
  867. RINOK(VolumeCallback->GetVolumeStream(_volIndex, &_volumeStream));
  868. _volIndex++;
  869. _curPos = 0;
  870. RINOK(_archive.Create(_volumeStream, true));
  871. RINOK(_archive.SkeepPrefixArchiveHeader());
  872. _crc.Init();
  873. continue;
  874. }
  875. UInt64 pureSize = COutArchive::GetVolPureSize(_volSize, _file.Name.Length());
  876. UInt32 curSize = (UInt32)MyMin(UInt64(size), pureSize - _curPos);
  877. _crc.Update(data, curSize);
  878. UInt32 realProcessed;
  879. RINOK(_volumeStream->Write(data, curSize, &realProcessed))
  880. data = (void *)((Byte *)data + realProcessed);
  881. size -= realProcessed;
  882. if(processedSize != NULL)
  883. *processedSize += realProcessed;
  884. _curPos += realProcessed;
  885. if (realProcessed != curSize && realProcessed == 0)
  886. return E_FAIL;
  887. if (_curPos == pureSize)
  888. {
  889. RINOK(Flush());
  890. }
  891. }
  892. return S_OK;
  893. }
  894. #endif
  895. HRESULT Update(
  896. DECL_EXTERNAL_CODECS_LOC_VARS
  897. IInStream *inStream,
  898. const CArchiveDatabaseEx *database,
  899. const CObjectVector<CUpdateItem> &updateItems,
  900. ISequentialOutStream *seqOutStream,
  901. IArchiveUpdateCallback *updateCallback,
  902. const CUpdateOptions &options)
  903. {
  904. #ifdef _7Z_VOL
  905. if (seqOutStream)
  906. #endif
  907. return Update2(
  908. EXTERNAL_CODECS_LOC_VARS
  909. inStream, database, updateItems,
  910. seqOutStream, updateCallback, options);
  911. #ifdef _7Z_VOL
  912. if (options.VolumeMode)
  913. return UpdateVolume(inStream, database, updateItems,
  914. seqOutStream, updateCallback, options);
  915. COutVolumeStream *volStreamSpec = new COutVolumeStream;
  916. CMyComPtr<ISequentialOutStream> volStream = volStreamSpec;
  917. CMyComPtr<IArchiveUpdateCallback2> volumeCallback;
  918. RINOK(updateCallback->QueryInterface(IID_IArchiveUpdateCallback2, (void **)&volumeCallback));
  919. if (!volumeCallback)
  920. return E_NOTIMPL;
  921. volStreamSpec->Init(volumeCallback, L"a.7z");
  922. volStreamSpec->_options = options;
  923. RINOK(Update2(inStream, database, updateItems,
  924. volStream, updateCallback, options));
  925. return volStreamSpec->Flush();
  926. #endif
  927. }
  928. }}