xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/ExtractCallback.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // ExtractCallback.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/IntToString.h"
7 #include "../../../Common/Lang.h"
8 #include "../../../Common/StringConvert.h"
9 
10 #include "../../../Windows/ErrorMsg.h"
11 #include "../../../Windows/FileDir.h"
12 #include "../../../Windows/FileFind.h"
13 #include "../../../Windows/PropVariantConv.h"
14 
15 #include "../../Common/FilePathAutoRename.h"
16 #include "../../Common/StreamUtils.h"
17 #include "../Common/ExtractingFilePath.h"
18 
19 #ifndef Z7_SFX
20 #include "../Common/ZipRegistry.h"
21 #endif
22 
23 #include "../GUI/ExtractRes.h"
24 #include "resourceGui.h"
25 
26 #include "ExtractCallback.h"
27 #include "FormatUtils.h"
28 #include "LangUtils.h"
29 #include "MemDialog.h"
30 #include "OverwriteDialog.h"
31 #ifndef Z7_NO_CRYPTO
32 #include "PasswordDialog.h"
33 #endif
34 #include "PropertyName.h"
35 
36 using namespace NWindows;
37 using namespace NFile;
38 using namespace NFind;
39 
40 extern bool g_DisableUserQuestions;
41 
~CExtractCallbackImp()42 CExtractCallbackImp::~CExtractCallbackImp() {}
43 
Init()44 void CExtractCallbackImp::Init()
45 {
46   LangString(IDS_PROGRESS_EXTRACTING, _lang_Extracting);
47   LangString(IDS_PROGRESS_TESTING, _lang_Testing);
48   LangString(IDS_PROGRESS_SKIPPING, _lang_Skipping);
49   _lang_Reading = "Reading";
50 
51   NumArchiveErrors = 0;
52   ThereAreMessageErrors = false;
53   #ifndef Z7_SFX
54   NumFolders = NumFiles = 0;
55   NeedAddFile = false;
56   #endif
57 }
58 
AddError_Message(LPCWSTR s)59 void CExtractCallbackImp::AddError_Message(LPCWSTR s)
60 {
61   ThereAreMessageErrors = true;
62   ProgressDialog->Sync.AddError_Message(s);
63 }
64 
AddError_Message_ShowArcPath(LPCWSTR s)65 void CExtractCallbackImp::AddError_Message_ShowArcPath(LPCWSTR s)
66 {
67   Add_ArchiveName_Error();
68   AddError_Message(s);
69 }
70 
71 
72 #ifndef Z7_SFX
73 
Z7_COM7F_IMF(CExtractCallbackImp::SetNumFiles (UInt64 numFiles))74 Z7_COM7F_IMF(CExtractCallbackImp::SetNumFiles(UInt64 numFiles))
75 {
76  #ifdef Z7_SFX
77   UNUSED_VAR(numFiles)
78  #else
79   ProgressDialog->Sync.Set_NumFilesTotal(numFiles);
80  #endif
81   return S_OK;
82 }
83 
84 #endif
85 
Z7_COM7F_IMF(CExtractCallbackImp::SetTotal (UInt64 total))86 Z7_COM7F_IMF(CExtractCallbackImp::SetTotal(UInt64 total))
87 {
88   ProgressDialog->Sync.Set_NumBytesTotal(total);
89   return S_OK;
90 }
91 
Z7_COM7F_IMF(CExtractCallbackImp::SetCompleted (const UInt64 * value))92 Z7_COM7F_IMF(CExtractCallbackImp::SetCompleted(const UInt64 *value))
93 {
94   return ProgressDialog->Sync.Set_NumBytesCur(value);
95 }
96 
Open_CheckBreak()97 HRESULT CExtractCallbackImp::Open_CheckBreak()
98 {
99   return ProgressDialog->Sync.CheckStop();
100 }
101 
Open_SetTotal(const UInt64 * files,const UInt64 * bytes)102 HRESULT CExtractCallbackImp::Open_SetTotal(const UInt64 *files, const UInt64 *bytes)
103 {
104   HRESULT res = S_OK;
105   if (!MultiArcMode)
106   {
107     if (files)
108     {
109       _totalFiles_Defined = true;
110       // res = ProgressDialog->Sync.Set_NumFilesTotal(*files);
111     }
112     else
113       _totalFiles_Defined = false;
114 
115     if (bytes)
116     {
117       _totalBytes_Defined = true;
118       ProgressDialog->Sync.Set_NumBytesTotal(*bytes);
119     }
120     else
121       _totalBytes_Defined = false;
122   }
123 
124   return res;
125 }
126 
Open_SetCompleted(const UInt64 * files,const UInt64 * bytes)127 HRESULT CExtractCallbackImp::Open_SetCompleted(const UInt64 *files, const UInt64 *bytes)
128 {
129   if (!MultiArcMode)
130   {
131     if (files)
132     {
133       ProgressDialog->Sync.Set_NumFilesCur(*files);
134     }
135 
136     if (bytes)
137     {
138     }
139   }
140 
141   return ProgressDialog->Sync.CheckStop();
142 }
143 
Open_Finished()144 HRESULT CExtractCallbackImp::Open_Finished()
145 {
146   return ProgressDialog->Sync.CheckStop();
147 }
148 
149 #ifndef Z7_NO_CRYPTO
150 
Open_CryptoGetTextPassword(BSTR * password)151 HRESULT CExtractCallbackImp::Open_CryptoGetTextPassword(BSTR *password)
152 {
153   return CryptoGetTextPassword(password);
154 }
155 
156 /*
157 HRESULT CExtractCallbackImp::Open_GetPasswordIfAny(bool &passwordIsDefined, UString &password)
158 {
159   passwordIsDefined = PasswordIsDefined;
160   password = Password;
161   return S_OK;
162 }
163 
164 bool CExtractCallbackImp::Open_WasPasswordAsked()
165 {
166   return PasswordWasAsked;
167 }
168 
169 void CExtractCallbackImp::Open_Clear_PasswordWasAsked_Flag()
170 {
171   PasswordWasAsked = false;
172 }
173 */
174 
175 #endif
176 
177 
178 #ifndef Z7_SFX
Z7_COM7F_IMF(CExtractCallbackImp::SetRatioInfo (const UInt64 * inSize,const UInt64 * outSize))179 Z7_COM7F_IMF(CExtractCallbackImp::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize))
180 {
181   ProgressDialog->Sync.Set_Ratio(inSize, outSize);
182   return S_OK;
183 }
184 #endif
185 
186 /*
187 Z7_COM7F_IMF(CExtractCallbackImp::SetTotalFiles(UInt64 total)
188 {
189   ProgressDialog->Sync.SetNumFilesTotal(total);
190   return S_OK;
191 }
192 
193 Z7_COM7F_IMF(CExtractCallbackImp::SetCompletedFiles(const UInt64 *value)
194 {
195   if (value != NULL)
196     ProgressDialog->Sync.SetNumFilesCur(*value);
197   return S_OK;
198 }
199 */
200 
Z7_COM7F_IMF(CExtractCallbackImp::AskOverwrite (const wchar_t * existName,const FILETIME * existTime,const UInt64 * existSize,const wchar_t * newName,const FILETIME * newTime,const UInt64 * newSize,Int32 * answer))201 Z7_COM7F_IMF(CExtractCallbackImp::AskOverwrite(
202     const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize,
203     const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize,
204     Int32 *answer))
205 {
206   COverwriteDialog dialog;
207 
208   dialog.OldFileInfo.SetTime2(existTime);
209   dialog.OldFileInfo.SetSize2(existSize);
210   dialog.OldFileInfo.Path = existName;
211   dialog.OldFileInfo.Is_FileSystemFile = true;
212 
213   dialog.NewFileInfo.SetTime2(newTime);
214   dialog.NewFileInfo.SetSize2(newSize);
215   dialog.NewFileInfo.Path = newName;
216   dialog.NewFileInfo.Is_FileSystemFile = Src_Is_IO_FS_Folder;
217 
218   ProgressDialog->WaitCreating();
219   const INT_PTR writeAnswer = dialog.Create(*ProgressDialog);
220 
221   switch (writeAnswer)
222   {
223     case IDCANCEL:        *answer = NOverwriteAnswer::kCancel; return E_ABORT;
224     case IDYES:           *answer = NOverwriteAnswer::kYes; break;
225     case IDNO:            *answer = NOverwriteAnswer::kNo; break;
226     case IDB_YES_TO_ALL:  *answer = NOverwriteAnswer::kYesToAll; break;
227     case IDB_NO_TO_ALL:   *answer = NOverwriteAnswer::kNoToAll; break;
228     case IDB_AUTO_RENAME: *answer = NOverwriteAnswer::kAutoRename; break;
229     default: return E_FAIL;
230   }
231   return S_OK;
232 }
233 
234 
Z7_COM7F_IMF(CExtractCallbackImp::PrepareOperation (const wchar_t * name,Int32 isFolder,Int32 askExtractMode,const UInt64 *))235 Z7_COM7F_IMF(CExtractCallbackImp::PrepareOperation(const wchar_t *name, Int32 isFolder, Int32 askExtractMode, const UInt64 * /* position */))
236 {
237   _isFolder = IntToBool(isFolder);
238   _currentFilePath = name;
239 
240   const UString *msg = &_lang_Empty;
241   switch (askExtractMode)
242   {
243     case NArchive::NExtract::NAskMode::kExtract: msg = &_lang_Extracting; break;
244     case NArchive::NExtract::NAskMode::kTest:    msg = &_lang_Testing; break;
245     case NArchive::NExtract::NAskMode::kSkip:    msg = &_lang_Skipping; break;
246     case NArchive::NExtract::NAskMode::kReadExternal: msg = &_lang_Reading; break;
247     // default: s = "Unknown operation";
248   }
249 
250   return ProgressDialog->Sync.Set_Status2(*msg, name, IntToBool(isFolder));
251 }
252 
Z7_COM7F_IMF(CExtractCallbackImp::MessageError (const wchar_t * s))253 Z7_COM7F_IMF(CExtractCallbackImp::MessageError(const wchar_t *s))
254 {
255   AddError_Message(s);
256   return S_OK;
257 }
258 
MessageError(const char * message,const FString & path)259 HRESULT CExtractCallbackImp::MessageError(const char *message, const FString &path)
260 {
261   ThereAreMessageErrors = true;
262   ProgressDialog->Sync.AddError_Message_Name(GetUnicodeString(message), fs2us(path));
263   return S_OK;
264 }
265 
266 #ifndef Z7_SFX
267 
Z7_COM7F_IMF(CExtractCallbackImp::ShowMessage (const wchar_t * s))268 Z7_COM7F_IMF(CExtractCallbackImp::ShowMessage(const wchar_t *s))
269 {
270   AddError_Message(s);
271   return S_OK;
272 }
273 
274 #endif
275 
276 void SetExtractErrorMessage(Int32 opRes, Int32 encrypted, const wchar_t *fileName, UString &s);
SetExtractErrorMessage(Int32 opRes,Int32 encrypted,const wchar_t * fileName,UString & s)277 void SetExtractErrorMessage(Int32 opRes, Int32 encrypted, const wchar_t *fileName, UString &s)
278 {
279   s.Empty();
280 
281   if (opRes == NArchive::NExtract::NOperationResult::kOK)
282     return;
283 
284  #ifndef Z7_SFX
285   UINT messageID = 0;
286  #endif
287   UINT id = 0;
288 
289   switch (opRes)
290   {
291     case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
292      #ifndef Z7_SFX
293       messageID = IDS_EXTRACT_MESSAGE_UNSUPPORTED_METHOD;
294      #endif
295       id = IDS_EXTRACT_MSG_UNSUPPORTED_METHOD;
296       break;
297     case NArchive::NExtract::NOperationResult::kDataError:
298      #ifndef Z7_SFX
299       messageID = encrypted ?
300           IDS_EXTRACT_MESSAGE_DATA_ERROR_ENCRYPTED:
301           IDS_EXTRACT_MESSAGE_DATA_ERROR;
302      #endif
303       id = IDS_EXTRACT_MSG_DATA_ERROR;
304       break;
305     case NArchive::NExtract::NOperationResult::kCRCError:
306      #ifndef Z7_SFX
307       messageID = encrypted ?
308           IDS_EXTRACT_MESSAGE_CRC_ERROR_ENCRYPTED:
309           IDS_EXTRACT_MESSAGE_CRC_ERROR;
310      #endif
311       id = IDS_EXTRACT_MSG_CRC_ERROR;
312       break;
313     case NArchive::NExtract::NOperationResult::kUnavailable:
314       id = IDS_EXTRACT_MSG_UNAVAILABLE_DATA;
315       break;
316     case NArchive::NExtract::NOperationResult::kUnexpectedEnd:
317       id = IDS_EXTRACT_MSG_UEXPECTED_END;
318       break;
319     case NArchive::NExtract::NOperationResult::kDataAfterEnd:
320       id = IDS_EXTRACT_MSG_DATA_AFTER_END;
321       break;
322     case NArchive::NExtract::NOperationResult::kIsNotArc:
323       id = IDS_EXTRACT_MSG_IS_NOT_ARC;
324       break;
325     case NArchive::NExtract::NOperationResult::kHeadersError:
326       id = IDS_EXTRACT_MSG_HEADERS_ERROR;
327       break;
328     case NArchive::NExtract::NOperationResult::kWrongPassword:
329       id = IDS_EXTRACT_MSG_WRONG_PSW_CLAIM;
330       break;
331     /*
332     default:
333       messageID = IDS_EXTRACT_MESSAGE_UNKNOWN_ERROR;
334       break;
335     */
336   }
337 
338   UString msg;
339 
340  #ifndef Z7_SFX
341   UString msgOld;
342  #ifdef Z7_LANG
343   if (id != 0)
344     LangString_OnlyFromLangFile(id, msg);
345   if (messageID != 0 && msg.IsEmpty())
346     LangString_OnlyFromLangFile(messageID, msgOld);
347  #endif
348   if (msg.IsEmpty() && !msgOld.IsEmpty())
349     s = MyFormatNew(msgOld, fileName);
350   else
351  #endif
352   {
353     if (msg.IsEmpty() && id != 0)
354       LangString(id, msg);
355     if (!msg.IsEmpty())
356       s += msg;
357     else
358     {
359       s += "Error #";
360       s.Add_UInt32((UInt32)opRes);
361     }
362 
363     if (encrypted && opRes != NArchive::NExtract::NOperationResult::kWrongPassword)
364     {
365       // s += " : ";
366       // AddLangString(s, IDS_EXTRACT_MSG_ENCRYPTED);
367       s += " : ";
368       AddLangString(s, IDS_EXTRACT_MSG_WRONG_PSW_GUESS);
369     }
370     s += " : ";
371     s += fileName;
372   }
373 }
374 
Z7_COM7F_IMF(CExtractCallbackImp::SetOperationResult (Int32 opRes,Int32 encrypted))375 Z7_COM7F_IMF(CExtractCallbackImp::SetOperationResult(Int32 opRes, Int32 encrypted))
376 {
377   switch (opRes)
378   {
379     case NArchive::NExtract::NOperationResult::kOK:
380       break;
381     default:
382     {
383       UString s;
384       SetExtractErrorMessage(opRes, encrypted, _currentFilePath, s);
385       AddError_Message_ShowArcPath(s);
386     }
387   }
388 
389   _currentFilePath.Empty();
390   #ifndef Z7_SFX
391   if (_isFolder)
392     NumFolders++;
393   else
394     NumFiles++;
395   ProgressDialog->Sync.Set_NumFilesCur(NumFiles);
396   #endif
397 
398   return S_OK;
399 }
400 
Z7_COM7F_IMF(CExtractCallbackImp::ReportExtractResult (Int32 opRes,Int32 encrypted,const wchar_t * name))401 Z7_COM7F_IMF(CExtractCallbackImp::ReportExtractResult(Int32 opRes, Int32 encrypted, const wchar_t *name))
402 {
403   if (opRes != NArchive::NExtract::NOperationResult::kOK)
404   {
405     UString s;
406     SetExtractErrorMessage(opRes, encrypted, name, s);
407     AddError_Message_ShowArcPath(s);
408   }
409   return S_OK;
410 }
411 
412 ////////////////////////////////////////
413 // IExtractCallbackUI
414 
BeforeOpen(const wchar_t * name,bool)415 HRESULT CExtractCallbackImp::BeforeOpen(const wchar_t *name, bool /* testMode */)
416 {
417   _currentArchivePath = name;
418   _needWriteArchivePath = true;
419   #ifndef Z7_SFX
420   RINOK(ProgressDialog->Sync.CheckStop())
421   ProgressDialog->Sync.Set_TitleFileName(name);
422   #endif
423   return S_OK;
424 }
425 
SetCurrentFilePath2(const wchar_t * path)426 HRESULT CExtractCallbackImp::SetCurrentFilePath2(const wchar_t *path)
427 {
428   _currentFilePath = path;
429   #ifndef Z7_SFX
430   ProgressDialog->Sync.Set_FilePath(path);
431   #endif
432   return S_OK;
433 }
434 
435 #ifndef Z7_SFX
436 
Z7_COM7F_IMF(CExtractCallbackImp::SetCurrentFilePath (const wchar_t * path))437 Z7_COM7F_IMF(CExtractCallbackImp::SetCurrentFilePath(const wchar_t *path))
438 {
439   #ifndef Z7_SFX
440   if (NeedAddFile)
441     NumFiles++;
442   NeedAddFile = true;
443   ProgressDialog->Sync.Set_NumFilesCur(NumFiles);
444   #endif
445   return SetCurrentFilePath2(path);
446 }
447 
448 #endif
449 
450 UString HResultToMessage(HRESULT errorCode);
451 
452 static const UInt32 k_ErrorFlagsIds[] =
453 {
454   IDS_EXTRACT_MSG_IS_NOT_ARC,
455   IDS_EXTRACT_MSG_HEADERS_ERROR,
456   IDS_EXTRACT_MSG_HEADERS_ERROR,
457   IDS_OPEN_MSG_UNAVAILABLE_START,
458   IDS_OPEN_MSG_UNCONFIRMED_START,
459   IDS_EXTRACT_MSG_UEXPECTED_END,
460   IDS_EXTRACT_MSG_DATA_AFTER_END,
461   IDS_EXTRACT_MSG_UNSUPPORTED_METHOD,
462   IDS_OPEN_MSG_UNSUPPORTED_FEATURE,
463   IDS_EXTRACT_MSG_DATA_ERROR,
464   IDS_EXTRACT_MSG_CRC_ERROR
465 };
466 
AddNewLineString(UString & s,const UString & m)467 static void AddNewLineString(UString &s, const UString &m)
468 {
469   s += m;
470   s.Add_LF();
471 }
472 
473 UString GetOpenArcErrorMessage(UInt32 errorFlags);
GetOpenArcErrorMessage(UInt32 errorFlags)474 UString GetOpenArcErrorMessage(UInt32 errorFlags)
475 {
476   UString s;
477 
478   for (unsigned i = 0; i < Z7_ARRAY_SIZE(k_ErrorFlagsIds); i++)
479   {
480     const UInt32 f = (UInt32)1 << i;
481     if ((errorFlags & f) == 0)
482       continue;
483     const UInt32 id = k_ErrorFlagsIds[i];
484     UString m = LangString(id);
485     if (m.IsEmpty())
486       continue;
487     if (f == kpv_ErrorFlags_EncryptedHeadersError)
488     {
489       m += " : ";
490       AddLangString(m, IDS_EXTRACT_MSG_WRONG_PSW_GUESS);
491     }
492     if (!s.IsEmpty())
493       s.Add_LF();
494     s += m;
495     errorFlags &= ~f;
496   }
497 
498   if (errorFlags != 0)
499   {
500     char sz[16];
501     sz[0] = '0';
502     sz[1] = 'x';
503     ConvertUInt32ToHex(errorFlags, sz + 2);
504     if (!s.IsEmpty())
505       s.Add_LF();
506     s += sz;
507   }
508 
509   return s;
510 }
511 
ErrorInfo_Print(UString & s,const CArcErrorInfo & er)512 static void ErrorInfo_Print(UString &s, const CArcErrorInfo &er)
513 {
514   const UInt32 errorFlags = er.GetErrorFlags();
515   const UInt32 warningFlags = er.GetWarningFlags();
516 
517   if (errorFlags != 0)
518     AddNewLineString(s, GetOpenArcErrorMessage(errorFlags));
519 
520   if (!er.ErrorMessage.IsEmpty())
521     AddNewLineString(s, er.ErrorMessage);
522 
523   if (warningFlags != 0)
524   {
525     s += GetNameOfProperty(kpidWarningFlags, L"Warnings");
526     s.Add_Colon();
527     s.Add_LF();
528     AddNewLineString(s, GetOpenArcErrorMessage(warningFlags));
529   }
530 
531   if (!er.WarningMessage.IsEmpty())
532   {
533     s += GetNameOfProperty(kpidWarning, L"Warning");
534     s += ": ";
535     s += er.WarningMessage;
536     s.Add_LF();
537   }
538 }
539 
GetBracedType(const wchar_t * type)540 static UString GetBracedType(const wchar_t *type)
541 {
542   UString s ('[');
543   s += type;
544   s.Add_Char(']');
545   return s;
546 }
547 
548 void OpenResult_GUI(UString &s, const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result);
OpenResult_GUI(UString & s,const CCodecs * codecs,const CArchiveLink & arcLink,const wchar_t * name,HRESULT result)549 void OpenResult_GUI(UString &s, const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result)
550 {
551   FOR_VECTOR (level, arcLink.Arcs)
552   {
553     const CArc &arc = arcLink.Arcs[level];
554     const CArcErrorInfo &er = arc.ErrorInfo;
555 
556     if (!er.IsThereErrorOrWarning() && er.ErrorFormatIndex < 0)
557       continue;
558 
559     if (s.IsEmpty())
560     {
561       s += name;
562       s.Add_LF();
563     }
564 
565     if (level != 0)
566     {
567       AddNewLineString(s, arc.Path);
568     }
569 
570     ErrorInfo_Print(s, er);
571 
572     if (er.ErrorFormatIndex >= 0)
573     {
574       AddNewLineString(s, GetNameOfProperty(kpidWarning, L"Warning"));
575       if (arc.FormatIndex == er.ErrorFormatIndex)
576       {
577         AddNewLineString(s, LangString(IDS_IS_OPEN_WITH_OFFSET));
578       }
579       else
580       {
581         AddNewLineString(s, MyFormatNew(IDS_CANT_OPEN_AS_TYPE, GetBracedType(codecs->GetFormatNamePtr(er.ErrorFormatIndex))));
582         AddNewLineString(s, MyFormatNew(IDS_IS_OPEN_AS_TYPE, GetBracedType(codecs->GetFormatNamePtr(arc.FormatIndex))));
583       }
584     }
585   }
586 
587   if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0 || result != S_OK)
588   {
589     s += name;
590     s.Add_LF();
591     if (!arcLink.Arcs.IsEmpty())
592       AddNewLineString(s, arcLink.NonOpen_ArcPath);
593 
594     if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0 || result == S_FALSE)
595     {
596       UINT id = IDS_CANT_OPEN_ARCHIVE;
597       UString param;
598       if (arcLink.PasswordWasAsked)
599         id = IDS_CANT_OPEN_ENCRYPTED_ARCHIVE;
600       else if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0)
601       {
602         id = IDS_CANT_OPEN_AS_TYPE;
603         param = GetBracedType(codecs->GetFormatNamePtr(arcLink.NonOpen_ErrorInfo.ErrorFormatIndex));
604       }
605       UString s2 = MyFormatNew(id, param);
606       s2.Replace(L" ''", L"");
607       s2.Replace(L"''", L"");
608       s += s2;
609     }
610     else
611       s += HResultToMessage(result);
612 
613     s.Add_LF();
614     ErrorInfo_Print(s, arcLink.NonOpen_ErrorInfo);
615   }
616 
617   if (!s.IsEmpty() && s.Back() == '\n')
618     s.DeleteBack();
619 }
620 
OpenResult(const CCodecs * codecs,const CArchiveLink & arcLink,const wchar_t * name,HRESULT result)621 HRESULT CExtractCallbackImp::OpenResult(const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result)
622 {
623   _currentArchivePath = name;
624   _needWriteArchivePath = true;
625 
626   UString s;
627   OpenResult_GUI(s, codecs, arcLink, name, result);
628   if (!s.IsEmpty())
629   {
630     NumArchiveErrors++;
631     AddError_Message(s);
632     _needWriteArchivePath = false;
633   }
634 
635   return S_OK;
636 }
637 
ThereAreNoFiles()638 HRESULT CExtractCallbackImp::ThereAreNoFiles()
639 {
640   return S_OK;
641 }
642 
Add_ArchiveName_Error()643 void CExtractCallbackImp::Add_ArchiveName_Error()
644 {
645   if (_needWriteArchivePath)
646   {
647     if (!_currentArchivePath.IsEmpty())
648       AddError_Message(_currentArchivePath);
649     _needWriteArchivePath = false;
650   }
651 }
652 
ExtractResult(HRESULT result)653 HRESULT CExtractCallbackImp::ExtractResult(HRESULT result)
654 {
655   #ifndef Z7_SFX
656   ProgressDialog->Sync.Set_FilePath(L"");
657   #endif
658 
659   if (result == S_OK)
660     return result;
661   NumArchiveErrors++;
662   if (result == E_ABORT
663       || result == HRESULT_FROM_WIN32(ERROR_DISK_FULL)
664       )
665     return result;
666 
667   Add_ArchiveName_Error();
668   if (!_currentFilePath.IsEmpty())
669     MessageError(_currentFilePath);
670   MessageError(NError::MyFormatMessage(result));
671   return S_OK;
672 }
673 
674 #ifndef Z7_NO_CRYPTO
675 
SetPassword(const UString & password)676 HRESULT CExtractCallbackImp::SetPassword(const UString &password)
677 {
678   PasswordIsDefined = true;
679   Password = password;
680   return S_OK;
681 }
682 
Z7_COM7F_IMF(CExtractCallbackImp::CryptoGetTextPassword (BSTR * password))683 Z7_COM7F_IMF(CExtractCallbackImp::CryptoGetTextPassword(BSTR *password))
684 {
685   PasswordWasAsked = true;
686   if (!PasswordIsDefined)
687   {
688     CPasswordDialog dialog;
689     #ifndef Z7_SFX
690     const bool showPassword = NExtract::Read_ShowPassword();
691     dialog.ShowPassword = showPassword;
692     #endif
693     ProgressDialog->WaitCreating();
694     if (dialog.Create(*ProgressDialog) != IDOK)
695       return E_ABORT;
696     Password = dialog.Password;
697     PasswordIsDefined = true;
698     #ifndef Z7_SFX
699     if (dialog.ShowPassword != showPassword)
700       NExtract::Save_ShowPassword(dialog.ShowPassword);
701     #endif
702   }
703   return StringToBstr(Password, password);
704 }
705 
706 #endif
707 
708 #ifndef Z7_SFX
709 
Z7_COM7F_IMF(CExtractCallbackImp::AskWrite (const wchar_t * srcPath,Int32 srcIsFolder,const FILETIME * srcTime,const UInt64 * srcSize,const wchar_t * destPath,BSTR * destPathResult,Int32 * writeAnswer))710 Z7_COM7F_IMF(CExtractCallbackImp::AskWrite(
711     const wchar_t *srcPath, Int32 srcIsFolder,
712     const FILETIME *srcTime, const UInt64 *srcSize,
713     const wchar_t *destPath,
714     BSTR *destPathResult,
715     Int32 *writeAnswer))
716 {
717   UString destPathResultTemp = destPath;
718 
719   // RINOK(StringToBstr(destPath, destPathResult));
720 
721   *destPathResult = NULL;
722   *writeAnswer = BoolToInt(false);
723 
724   FString destPathSys = us2fs(destPath);
725   const bool srcIsFolderSpec = IntToBool(srcIsFolder);
726   CFileInfo destFileInfo;
727 
728   if (destFileInfo.Find(destPathSys))
729   {
730     if (srcIsFolderSpec)
731     {
732       if (!destFileInfo.IsDir())
733       {
734         RINOK(MessageError("Cannot replace file with folder with same name", destPathSys))
735         return E_ABORT;
736       }
737       *writeAnswer = BoolToInt(false);
738       return S_OK;
739     }
740 
741     if (destFileInfo.IsDir())
742     {
743       RINOK(MessageError("Cannot replace folder with file with same name", destPathSys))
744       *writeAnswer = BoolToInt(false);
745       return S_OK;
746     }
747 
748     switch ((int)OverwriteMode)
749     {
750       case NExtract::NOverwriteMode::kSkip:
751         return S_OK;
752       case NExtract::NOverwriteMode::kAsk:
753       {
754         Int32 overwriteResult;
755         UString destPathSpec = destPath;
756         const int slashPos = destPathSpec.ReverseFind_PathSepar();
757         destPathSpec.DeleteFrom((unsigned)(slashPos + 1));
758         destPathSpec += fs2us(destFileInfo.Name);
759 
760         RINOK(AskOverwrite(
761             destPathSpec,
762             &destFileInfo.MTime, &destFileInfo.Size,
763             srcPath,
764             srcTime, srcSize,
765             &overwriteResult))
766 
767         switch (overwriteResult)
768         {
769           case NOverwriteAnswer::kCancel: return E_ABORT;
770           case NOverwriteAnswer::kNo: return S_OK;
771           case NOverwriteAnswer::kNoToAll: OverwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK;
772           case NOverwriteAnswer::kYes: break;
773           case NOverwriteAnswer::kYesToAll: OverwriteMode = NExtract::NOverwriteMode::kOverwrite; break;
774           case NOverwriteAnswer::kAutoRename: OverwriteMode = NExtract::NOverwriteMode::kRename; break;
775           default:
776             return E_FAIL;
777         }
778         break;
779       }
780       default:
781         break;
782     }
783 
784     if (OverwriteMode == NExtract::NOverwriteMode::kRename)
785     {
786       if (!AutoRenamePath(destPathSys))
787       {
788         RINOK(MessageError("Cannot create name for file", destPathSys))
789         return E_ABORT;
790       }
791       destPathResultTemp = fs2us(destPathSys);
792     }
793     else
794     {
795       if (NFind::DoesFileExist_Raw(destPathSys))
796       if (!NDir::DeleteFileAlways(destPathSys))
797       if (GetLastError() != ERROR_FILE_NOT_FOUND)
798       {
799         RINOK(MessageError("Cannot delete output file", destPathSys))
800         return E_ABORT;
801       }
802     }
803   }
804   *writeAnswer = BoolToInt(true);
805   return StringToBstr(destPathResultTemp, destPathResult);
806 }
807 
808 
Z7_COM7F_IMF(CExtractCallbackImp::UseExtractToStream (Int32 * res))809 Z7_COM7F_IMF(CExtractCallbackImp::UseExtractToStream(Int32 *res))
810 {
811   *res = BoolToInt(StreamMode);
812   return S_OK;
813 }
814 
GetTime(IGetProp * getProp,PROPID propID,FILETIME & ft,bool & ftDefined)815 static HRESULT GetTime(IGetProp *getProp, PROPID propID, FILETIME &ft, bool &ftDefined)
816 {
817   ftDefined = false;
818   NCOM::CPropVariant prop;
819   RINOK(getProp->GetProp(propID, &prop))
820   if (prop.vt == VT_FILETIME)
821   {
822     ft = prop.filetime;
823     ftDefined = (ft.dwHighDateTime != 0 || ft.dwLowDateTime != 0);
824   }
825   else if (prop.vt != VT_EMPTY)
826     return E_FAIL;
827   return S_OK;
828 }
829 
830 
GetItemBoolProp(IGetProp * getProp,PROPID propID,bool & result)831 static HRESULT GetItemBoolProp(IGetProp *getProp, PROPID propID, bool &result)
832 {
833   NCOM::CPropVariant prop;
834   result = false;
835   RINOK(getProp->GetProp(propID, &prop))
836   if (prop.vt == VT_BOOL)
837     result = VARIANT_BOOLToBool(prop.boolVal);
838   else if (prop.vt != VT_EMPTY)
839     return E_FAIL;
840   return S_OK;
841 }
842 
843 
Z7_COM7F_IMF(CExtractCallbackImp::GetStream7 (const wchar_t * name,Int32 isDir,ISequentialOutStream ** outStream,Int32 askExtractMode,IGetProp * getProp))844 Z7_COM7F_IMF(CExtractCallbackImp::GetStream7(const wchar_t *name,
845     Int32 isDir,
846     ISequentialOutStream **outStream, Int32 askExtractMode,
847     IGetProp *getProp))
848 {
849   COM_TRY_BEGIN
850   *outStream = NULL;
851   _newVirtFileWasAdded = false;
852   _hashStream_WasUsed = false;
853   _needUpdateStat = false;
854   _isFolder = IntToBool(isDir);
855   _curSize_Defined = false;
856   _curSize = 0;
857 
858   if (_hashStream)
859     _hashStream->ReleaseStream();
860 
861   _filePath = name;
862 
863   UInt64 size = 0;
864   bool size_Defined;
865   {
866     NCOM::CPropVariant prop;
867     RINOK(getProp->GetProp(kpidSize, &prop))
868     size_Defined = ConvertPropVariantToUInt64(prop, size);
869   }
870   if (size_Defined)
871   {
872     _curSize = size;
873     _curSize_Defined = true;
874   }
875 
876   GetItemBoolProp(getProp, kpidIsAltStream, _isAltStream);
877   if (!ProcessAltStreams && _isAltStream)
878     return S_OK;
879 
880   if (isDir) // we don't support dir items extraction in this code
881     return S_OK;
882 
883   if (askExtractMode != NArchive::NExtract::NAskMode::kExtract &&
884       askExtractMode != NArchive::NExtract::NAskMode::kTest)
885     return S_OK;
886 
887   _needUpdateStat = true;
888 
889   CMyComPtr<ISequentialOutStream> outStreamLoc;
890 
891   if (VirtFileSystem && askExtractMode == NArchive::NExtract::NAskMode::kExtract)
892   {
893     if (!VirtFileSystemSpec->Files.IsEmpty())
894       VirtFileSystemSpec->MaxTotalAllocSize -= VirtFileSystemSpec->Files.Back().Data.Size();
895     CVirtFile &file = VirtFileSystemSpec->Files.AddNew();
896     _newVirtFileWasAdded = true;
897     // file.IsDir = _isFolder;
898     file.IsAltStream = _isAltStream;
899     file.WrittenSize = 0;
900     file.ExpectedSize = 0;
901     if (size_Defined)
902       file.ExpectedSize = size;
903 
904     if (VirtFileSystemSpec->Index_of_MainExtractedFile_in_Files < 0)
905       if (!file.IsAltStream || VirtFileSystemSpec->IsAltStreamFile)
906         VirtFileSystemSpec->Index_of_MainExtractedFile_in_Files =
907             (int)(VirtFileSystemSpec->Files.Size() - 1);
908 
909     /* if we open only AltStream, then (name) contains only name without "fileName:" prefix */
910     file.BaseName = name;
911 
912     if (file.IsAltStream
913         && !VirtFileSystemSpec->IsAltStreamFile
914         && file.BaseName.IsPrefixedBy_NoCase(VirtFileSystemSpec->FileName))
915     {
916       const unsigned colonPos = VirtFileSystemSpec->FileName.Len();
917       if (file.BaseName[colonPos] == ':')
918       {
919         file.ColonWasUsed = true;
920         file.AltStreamName = name + (size_t)colonPos + 1;
921         file.BaseName.DeleteFrom(colonPos);
922         if (Is_ZoneId_StreamName(file.AltStreamName))
923         {
924           if (VirtFileSystemSpec->Index_of_ZoneBuf_AltStream_in_Files < 0)
925             VirtFileSystemSpec->Index_of_ZoneBuf_AltStream_in_Files =
926               (int)(VirtFileSystemSpec->Files.Size() - 1);
927         }
928       }
929     }
930     RINOK(GetTime(getProp, kpidCTime, file.CTime, file.CTime_Defined))
931     RINOK(GetTime(getProp, kpidATime, file.ATime, file.ATime_Defined))
932     RINOK(GetTime(getProp, kpidMTime, file.MTime, file.MTime_Defined))
933     {
934       NCOM::CPropVariant prop;
935       RINOK(getProp->GetProp(kpidAttrib, &prop))
936       if (prop.vt == VT_UI4)
937       {
938         file.Attrib = prop.ulVal;
939         file.Attrib_Defined = true;
940       }
941     }
942     outStreamLoc = VirtFileSystem;
943   }
944 
945   if (_hashStream)
946   {
947     _hashStream->SetStream(outStreamLoc);
948     outStreamLoc = _hashStream;
949     _hashStream->Init(true);
950     _hashStream_WasUsed = true;
951   }
952 
953   if (outStreamLoc)
954     *outStream = outStreamLoc.Detach();
955   return S_OK;
956   COM_TRY_END
957 }
958 
Z7_COM7F_IMF(CExtractCallbackImp::PrepareOperation7 (Int32 askExtractMode))959 Z7_COM7F_IMF(CExtractCallbackImp::PrepareOperation7(Int32 askExtractMode))
960 {
961   COM_TRY_BEGIN
962   _needUpdateStat = (
963          askExtractMode == NArchive::NExtract::NAskMode::kExtract
964       || askExtractMode == NArchive::NExtract::NAskMode::kTest
965       || askExtractMode == NArchive::NExtract::NAskMode::kReadExternal
966       );
967 
968   /*
969   _extractMode = false;
970   switch (askExtractMode)
971   {
972     case NArchive::NExtract::NAskMode::kExtract:
973       if (_testMode)
974         askExtractMode = NArchive::NExtract::NAskMode::kTest;
975       else
976         _extractMode = true;
977       break;
978   };
979   */
980   return SetCurrentFilePath2(_filePath);
981   COM_TRY_END
982 }
983 
Z7_COM7F_IMF(CExtractCallbackImp::SetOperationResult8 (Int32 opRes,Int32 encrypted,UInt64 size))984 Z7_COM7F_IMF(CExtractCallbackImp::SetOperationResult8(Int32 opRes, Int32 encrypted, UInt64 size))
985 {
986   COM_TRY_BEGIN
987   if (VirtFileSystem && _newVirtFileWasAdded)
988   {
989     // FIXME: probably we must request file size from VirtFileSystem
990     // _curSize = VirtFileSystem->GetLastFileSize()
991     // _curSize_Defined = true;
992     RINOK(VirtFileSystemSpec->CloseMemFile())
993   }
994   if (_hashStream && _hashStream_WasUsed)
995   {
996     _hashStream->_hash->Final(_isFolder, _isAltStream, _filePath);
997     _curSize = _hashStream->GetSize();
998     _curSize_Defined = true;
999     _hashStream->ReleaseStream();
1000     _hashStream_WasUsed = false;
1001   }
1002   else if (_hashCalc && _needUpdateStat)
1003   {
1004     _hashCalc->SetSize(size); // (_curSize) before 21.04
1005     _hashCalc->Final(_isFolder, _isAltStream, _filePath);
1006   }
1007   return SetOperationResult(opRes, encrypted);
1008   COM_TRY_END
1009 }
1010 
1011 
Z7_COM7F_IMF(CExtractCallbackImp::RequestMemoryUse (UInt32 flags,UInt32 indexType,UInt32,const wchar_t * path,UInt64 requiredSize,UInt64 * allowedSize,UInt32 * answerFlags))1012 Z7_COM7F_IMF(CExtractCallbackImp::RequestMemoryUse(
1013     UInt32 flags, UInt32 indexType, UInt32 /* index */, const wchar_t *path,
1014     UInt64 requiredSize, UInt64 *allowedSize, UInt32 *answerFlags))
1015 {
1016   UInt32 limit_GB = (UInt32)((*allowedSize + ((1u << 30) - 1)) >> 30);
1017 
1018   if ((flags & NRequestMemoryUseFlags::k_IsReport) == 0)
1019   {
1020     UInt64 limit_bytes = *allowedSize;
1021     const UInt32 limit_GB_Registry = NExtract::Read_LimitGB();
1022     if (limit_GB_Registry != 0 && limit_GB_Registry != (UInt32)(Int32)-1)
1023     {
1024       const UInt64 limit_bytes_Registry = (UInt64)limit_GB_Registry << 30;
1025       // registry_WasForced = true;
1026       if ((flags & NRequestMemoryUseFlags::k_AllowedSize_WasForced) == 0
1027           || limit_bytes < limit_bytes_Registry)
1028       {
1029         limit_bytes = limit_bytes_Registry;
1030         limit_GB = limit_GB_Registry;
1031       }
1032     }
1033     *allowedSize = limit_bytes;
1034     if (requiredSize <= limit_bytes)
1035     {
1036       *answerFlags = NRequestMemoryAnswerFlags::k_Allow;
1037       return S_OK;
1038     }
1039     // default answer can be k_Allow, if limit was not forced,
1040     // so we change answer to non-allowed here,
1041     // because user has chance to change limit in GUI.
1042     *answerFlags = NRequestMemoryAnswerFlags::k_Limit_Exceeded;
1043     if (flags & NRequestMemoryUseFlags::k_SkipArc_IsExpected)
1044       *answerFlags |= NRequestMemoryAnswerFlags::k_SkipArc;
1045   }
1046 
1047   const UInt32 required_GB = (UInt32)((requiredSize + ((1u << 30) - 1)) >> 30);
1048 
1049   CMemDialog dialog;
1050   dialog.Limit_GB = limit_GB;
1051   dialog.Required_GB = required_GB;
1052   dialog.TestMode = TestMode;
1053   if (MultiArcMode)
1054     dialog.ArcPath = _currentArchivePath;
1055   if (path)
1056     dialog.FilePath = path;
1057 
1058   if (!g_DisableUserQuestions
1059       && (flags & NRequestMemoryUseFlags::k_IsReport) == 0)
1060   {
1061     if (_remember)
1062       dialog.SkipArc = _skipArc;
1063     else
1064     {
1065       dialog.ShowRemember =
1066         (MultiArcMode
1067           || indexType != NArchive::NEventIndexType::kNoIndex
1068           || path);
1069       ProgressDialog->WaitCreating();
1070       if (dialog.Create(*ProgressDialog) != IDCONTINUE)
1071       {
1072         *answerFlags = NRequestMemoryAnswerFlags::k_Stop;
1073         return E_ABORT;
1074       }
1075       if (dialog.NeedSave)
1076         NExtract::Save_LimitGB(dialog.Limit_GB);
1077       if (dialog.Remember)
1078       {
1079         _remember = true;
1080         _skipArc = dialog.SkipArc;
1081       }
1082     }
1083 
1084     *allowedSize = (UInt64)dialog.Limit_GB << 30;
1085     if (!dialog.SkipArc)
1086     {
1087       *answerFlags = NRequestMemoryAnswerFlags::k_Allow;
1088       return S_OK;
1089     }
1090     *answerFlags =
1091         NRequestMemoryAnswerFlags::k_SkipArc
1092       | NRequestMemoryAnswerFlags::k_Limit_Exceeded;
1093     flags |= NRequestMemoryUseFlags::k_Report_SkipArc;
1094   }
1095 
1096   if ((flags & NRequestMemoryUseFlags::k_NoErrorMessage) == 0)
1097   {
1098     UString s ("ERROR: ");
1099     dialog.AddInfoMessage_To_String(s);
1100     s.Add_LF();
1101     // if (indexType == NArchive::NEventIndexType::kNoIndex)
1102     if ((flags & NRequestMemoryUseFlags::k_SkipArc_IsExpected) ||
1103         (flags & NRequestMemoryUseFlags::k_Report_SkipArc))
1104       AddLangString(s, IDS_MSG_ARC_UNPACKING_WAS_SKIPPED);
1105 /*
1106     else
1107       AddLangString(, IDS_MSG_ARC_FILES_UNPACKING_WAS_SKIPPED);
1108 */
1109     AddError_Message_ShowArcPath(s);
1110   }
1111 
1112 /*
1113   if ((flags & NRequestMemoryUseFlags::k_IsReport) == 0)
1114     *answerFlags |= NRequestMemoryAnswerFlags::k_Limit_Exceeded;
1115 */
1116   return S_OK;
1117 }
1118 
1119 
Z7_COM7F_IMF(CVirtFileSystem::Write (const void * data,UInt32 size,UInt32 * processedSize))1120 Z7_COM7F_IMF(CVirtFileSystem::Write(const void *data, UInt32 size, UInt32 *processedSize))
1121 {
1122   if (processedSize)
1123     *processedSize = 0;
1124   if (size == 0)
1125     return S_OK;
1126   if (!_wasSwitchedToFsMode)
1127   {
1128     CVirtFile &file = Files.Back();
1129     const size_t rem = file.Data.Size() - file.WrittenSize;
1130     bool useMem = true;
1131     if (rem < size)
1132     {
1133       UInt64 b = 0;
1134       if (file.Data.Size() == 0)
1135         b = file.ExpectedSize;
1136       UInt64 a = (UInt64)file.WrittenSize + size;
1137       if (b < a)
1138         b = a;
1139       a = (UInt64)file.Data.Size() * 2;
1140       if (b < a)
1141         b = a;
1142       useMem = false;
1143       if (b <= MaxTotalAllocSize)
1144         useMem = file.Data.ReAlloc_KeepData((size_t)b, file.WrittenSize);
1145     }
1146 
1147 #if 0 // 1 for debug : FLUSHING TO FS
1148     useMem = false;
1149 #endif
1150 
1151     if (useMem)
1152     {
1153       memcpy(file.Data + file.WrittenSize, data, size);
1154       file.WrittenSize += size;
1155       if (processedSize)
1156         *processedSize = (UInt32)size;
1157       return S_OK;
1158     }
1159     _wasSwitchedToFsMode = true;
1160   }
1161 
1162   if (!_newVirtFileStream_IsReadyToWrite) // we check for _newVirtFileStream_IsReadyToWrite to optimize execution
1163   {
1164     RINOK(FlushToDisk(false))
1165   }
1166 
1167   if (_needWriteToRealFile)
1168     return _outFileStream.Interface()->Write(data, size, processedSize);
1169   if (processedSize)
1170     *processedSize = size;
1171   return S_OK;
1172 }
1173 
1174 
FlushToDisk(bool closeLast)1175 HRESULT CVirtFileSystem::FlushToDisk(bool closeLast)
1176 {
1177   while (_numFlushed < Files.Size())
1178   {
1179     CVirtFile &file = Files[_numFlushed];
1180     const FString basePath = DirPrefix + us2fs(Get_Correct_FsFile_Name(file.BaseName));
1181     FString path = basePath;
1182 
1183     if (file.ColonWasUsed)
1184     {
1185       if (ZoneBuf.Size() != 0
1186           && Is_ZoneId_StreamName(file.AltStreamName))
1187       {
1188         // it's expected that
1189         // CArchiveExtractCallback::GetStream() have excluded
1190         // ZoneId alt stream from extraction already.
1191         // But we exclude alt stream extraction here too.
1192         _numFlushed++;
1193         continue;
1194       }
1195       path.Add_Colon();
1196       path += us2fs(Get_Correct_FsFile_Name(file.AltStreamName));
1197     }
1198 
1199     if (!_newVirtFileStream_IsReadyToWrite)
1200     {
1201       if (file.ColonWasUsed)
1202       {
1203         NFind::CFileInfo parentFi;
1204         if (parentFi.Find(basePath)
1205             && parentFi.IsReadOnly())
1206         {
1207           _altStream_NeedRestore_Attrib_bool = true;
1208           _altStream_NeedRestore_AttribVal = parentFi.Attrib;
1209           NDir::SetFileAttrib(basePath, parentFi.Attrib & ~(DWORD)FILE_ATTRIBUTE_READONLY);
1210         }
1211       }
1212       _outFileStream.Create_if_Empty();
1213       _needWriteToRealFile = _outFileStream->Create_NEW(path);
1214       if (!_needWriteToRealFile)
1215       {
1216         if (!file.ColonWasUsed)
1217           return GetLastError_noZero_HRESULT(); // it's main file and we can't ignore such error.
1218         // (file.ColonWasUsed == true)
1219         // So it's additional alt stream.
1220         // And we ignore file creation error for additional alt stream.
1221         // ShowErrorMessage(UString("Can't create file ") + fs2us(path));
1222       }
1223       _newVirtFileStream_IsReadyToWrite = true;
1224       // _openFilePath = path;
1225       HRESULT hres = S_OK;
1226       if (_needWriteToRealFile)
1227         hres = WriteStream(_outFileStream, file.Data, file.WrittenSize);
1228       // we free allocated memory buffer after data flushing:
1229       file.WrittenSize = 0;
1230       file.Data.Free();
1231       RINOK(hres)
1232     }
1233 
1234     if (_numFlushed == Files.Size() - 1 && !closeLast)
1235       break;
1236 
1237     if (_needWriteToRealFile)
1238     {
1239       if (file.CTime_Defined ||
1240           file.ATime_Defined ||
1241           file.MTime_Defined)
1242         _outFileStream->SetTime(
1243           file.CTime_Defined ? &file.CTime : NULL,
1244           file.ATime_Defined ? &file.ATime : NULL,
1245           file.MTime_Defined ? &file.MTime : NULL);
1246       _outFileStream->Close();
1247     }
1248 
1249     _numFlushed++;
1250     _newVirtFileStream_IsReadyToWrite = false;
1251 
1252     if (_needWriteToRealFile)
1253     {
1254       if (!file.ColonWasUsed
1255           && ZoneBuf.Size() != 0)
1256         WriteZoneFile_To_BaseFile(path, ZoneBuf);
1257       if (file.Attrib_Defined)
1258         NDir::SetFileAttrib_PosixHighDetect(path, file.Attrib);
1259       // _openFilePath.Empty();
1260       _needWriteToRealFile = false;
1261     }
1262 
1263     if (_altStream_NeedRestore_Attrib_bool)
1264     {
1265       _altStream_NeedRestore_Attrib_bool = false;
1266       NDir::SetFileAttrib(basePath, _altStream_NeedRestore_AttribVal);
1267     }
1268   }
1269   return S_OK;
1270 }
1271 
1272 #endif
1273