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