1// ExtractCallback.cpp
2
3#include "StdAfx.h"
4
5
6#include "../../../Common/ComTry.h"
7#include "../../../Common/IntToString.h"
8#include "../../../Common/Lang.h"
9#include "../../../Common/StringConvert.h"
10
11#include "../../../Windows/ErrorMsg.h"
12#include "../../../Windows/FileDir.h"
13#include "../../../Windows/FileFind.h"
14#include "../../../Windows/PropVariantConv.h"
15
16#include "../../Common/FilePathAutoRename.h"
17#include "../../Common/StreamUtils.h"
18#include "../Common/ExtractingFilePath.h"
19
20#ifndef _SFX
21#include "../Common/ZipRegistry.h"
22#endif
23
24#include "../GUI/ExtractRes.h"
25
26#include "ExtractCallback.h"
27#include "FormatUtils.h"
28#include "LangUtils.h"
29#include "OverwriteDialog.h"
30#ifndef _NO_CRYPTO
31#include "PasswordDialog.h"
32#endif
33
34using namespace NWindows;
35using namespace NFile;
36using namespace NFind;
37
38CExtractCallbackImp::~CExtractCallbackImp() {}
39
40void CExtractCallbackImp::Init()
41{
42  NumArchiveErrors = 0;
43  ThereAreMessageErrors = false;
44  #ifndef _SFX
45  NumFolders = NumFiles = 0;
46  NeedAddFile = false;
47  #endif
48}
49
50void CExtractCallbackImp::AddError_Message(LPCWSTR s)
51{
52  ThereAreMessageErrors = true;
53  ProgressDialog->Sync.AddError_Message(s);
54}
55
56#ifndef _SFX
57
58STDMETHODIMP CExtractCallbackImp::SetNumFiles(UInt64
59  #ifndef _SFX
60  numFiles
61  #endif
62  )
63{
64  #ifndef _SFX
65  ProgressDialog->Sync.Set_NumFilesTotal(numFiles);
66  #endif
67  return S_OK;
68}
69
70#endif
71
72STDMETHODIMP CExtractCallbackImp::SetTotal(UInt64 total)
73{
74  ProgressDialog->Sync.Set_NumBytesTotal(total);
75  return S_OK;
76}
77
78STDMETHODIMP CExtractCallbackImp::SetCompleted(const UInt64 *value)
79{
80  return ProgressDialog->Sync.Set_NumBytesCur(value);
81}
82
83HRESULT CExtractCallbackImp::Open_CheckBreak()
84{
85  return ProgressDialog->Sync.CheckStop();
86}
87
88HRESULT CExtractCallbackImp::Open_SetTotal(const UInt64 * /* numFiles */, const UInt64 * /* numBytes */)
89{
90  // if (numFiles != NULL) ProgressDialog->Sync.SetNumFilesTotal(*numFiles);
91  return S_OK;
92}
93
94HRESULT CExtractCallbackImp::Open_SetCompleted(const UInt64 * /* numFiles */, const UInt64 * /* numBytes */)
95{
96  // if (numFiles != NULL) ProgressDialog->Sync.SetNumFilesCur(*numFiles);
97  return ProgressDialog->Sync.CheckStop();
98}
99
100#ifndef _NO_CRYPTO
101
102HRESULT CExtractCallbackImp::Open_CryptoGetTextPassword(BSTR *password)
103{
104  return CryptoGetTextPassword(password);
105}
106
107HRESULT CExtractCallbackImp::Open_GetPasswordIfAny(bool &passwordIsDefined, UString &password)
108{
109  passwordIsDefined = PasswordIsDefined;
110  password = Password;
111  return S_OK;
112}
113
114bool CExtractCallbackImp::Open_WasPasswordAsked()
115{
116  return PasswordWasAsked;
117}
118
119void CExtractCallbackImp::Open_ClearPasswordWasAskedFlag()
120{
121  PasswordWasAsked = false;
122}
123
124#endif
125
126
127#ifndef _SFX
128STDMETHODIMP CExtractCallbackImp::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
129{
130  ProgressDialog->Sync.Set_Ratio(inSize, outSize);
131  return S_OK;
132}
133#endif
134
135/*
136STDMETHODIMP CExtractCallbackImp::SetTotalFiles(UInt64 total)
137{
138  ProgressDialog->Sync.SetNumFilesTotal(total);
139  return S_OK;
140}
141
142STDMETHODIMP CExtractCallbackImp::SetCompletedFiles(const UInt64 *value)
143{
144  if (value != NULL)
145    ProgressDialog->Sync.SetNumFilesCur(*value);
146  return S_OK;
147}
148*/
149
150STDMETHODIMP CExtractCallbackImp::AskOverwrite(
151    const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize,
152    const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize,
153    Int32 *answer)
154{
155  COverwriteDialog dialog;
156
157  dialog.OldFileInfo.SetTime(existTime);
158  dialog.OldFileInfo.SetSize(existSize);
159  dialog.OldFileInfo.Name = existName;
160
161  dialog.NewFileInfo.SetTime(newTime);
162  dialog.NewFileInfo.SetSize(newSize);
163  dialog.NewFileInfo.Name = newName;
164
165  ProgressDialog->WaitCreating();
166  INT_PTR writeAnswer = dialog.Create(*ProgressDialog);
167
168  switch (writeAnswer)
169  {
170    case IDCANCEL:        *answer = NOverwriteAnswer::kCancel; return E_ABORT;
171    case IDYES:           *answer = NOverwriteAnswer::kYes; break;
172    case IDNO:            *answer = NOverwriteAnswer::kNo; break;
173    case IDB_YES_TO_ALL:  *answer = NOverwriteAnswer::kYesToAll; break;
174    case IDB_NO_TO_ALL:   *answer = NOverwriteAnswer::kNoToAll; break;
175    case IDB_AUTO_RENAME: *answer = NOverwriteAnswer::kAutoRename; break;
176    default: return E_FAIL;
177  }
178  return S_OK;
179}
180
181
182STDMETHODIMP CExtractCallbackImp::PrepareOperation(const wchar_t *name, bool isFolder, Int32 /* askExtractMode */, const UInt64 * /* position */)
183{
184  _isFolder = isFolder;
185  return SetCurrentFilePath2(name);
186}
187
188STDMETHODIMP CExtractCallbackImp::MessageError(const wchar_t *s)
189{
190  AddError_Message(s);
191  return S_OK;
192}
193
194HRESULT CExtractCallbackImp::MessageError(const char *message, const FString &path)
195{
196  ThereAreMessageErrors = true;
197  ProgressDialog->Sync.AddError_Message_Name(GetUnicodeString(message), fs2us(path));
198  return S_OK;
199}
200
201#ifndef _SFX
202
203STDMETHODIMP CExtractCallbackImp::ShowMessage(const wchar_t *s)
204{
205  AddError_Message(s);
206  return S_OK;
207}
208
209#endif
210
211STDMETHODIMP CExtractCallbackImp::SetOperationResult(Int32 opRes, bool encrypted)
212{
213  switch (opRes)
214  {
215    case NArchive::NExtract::NOperationResult::kOK:
216      break;
217    default:
218    {
219      UINT messageID = 0;
220      UINT id = 0;
221
222      switch (opRes)
223      {
224        case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
225          messageID = IDS_EXTRACT_MESSAGE_UNSUPPORTED_METHOD;
226          id = IDS_EXTRACT_MSG_UNSUPPORTED_METHOD;
227          break;
228        case NArchive::NExtract::NOperationResult::kDataError:
229          messageID = encrypted ?
230              IDS_EXTRACT_MESSAGE_DATA_ERROR_ENCRYPTED:
231              IDS_EXTRACT_MESSAGE_DATA_ERROR;
232          id = IDS_EXTRACT_MSG_DATA_ERROR;
233          break;
234        case NArchive::NExtract::NOperationResult::kCRCError:
235          messageID = encrypted ?
236              IDS_EXTRACT_MESSAGE_CRC_ERROR_ENCRYPTED:
237              IDS_EXTRACT_MESSAGE_CRC_ERROR;
238          id = IDS_EXTRACT_MSG_CRC_ERROR;
239          break;
240        case NArchive::NExtract::NOperationResult::kUnavailable:
241          id = IDS_EXTRACT_MSG_UNAVAILABLE_DATA;
242          break;
243        case NArchive::NExtract::NOperationResult::kUnexpectedEnd:
244          id = IDS_EXTRACT_MSG_UEXPECTED_END;
245          break;
246        case NArchive::NExtract::NOperationResult::kDataAfterEnd:
247          id = IDS_EXTRACT_MSG_DATA_AFTER_END;
248          break;
249        case NArchive::NExtract::NOperationResult::kIsNotArc:
250          id = IDS_EXTRACT_MSG_IS_NOT_ARC;
251          break;
252        case NArchive::NExtract::NOperationResult::kHeadersError:
253          id = IDS_EXTRACT_MSG_HEADERS_ERROR;
254          break;
255        /*
256        default:
257          messageID = IDS_EXTRACT_MESSAGE_UNKNOWN_ERROR;
258          break;
259        */
260      }
261      if (_needWriteArchivePath)
262      {
263        if (!_currentArchivePath.IsEmpty())
264          AddError_Message(_currentArchivePath);
265        _needWriteArchivePath = false;
266      }
267
268      UString msg;
269      UString msgOld;
270
271      #ifndef _SFX
272      if (id != 0)
273        LangString_OnlyFromLangFile(id, msg);
274      if (messageID != 0 && msg.IsEmpty())
275        LangString_OnlyFromLangFile(messageID, msgOld);
276      #endif
277
278      UString s;
279      if (msg.IsEmpty() && !msgOld.IsEmpty())
280        s = MyFormatNew(msgOld, _currentFilePath);
281      else
282      {
283        if (msg.IsEmpty())
284          LangString(id, msg);
285        if (!msg.IsEmpty())
286          s += msg;
287        else
288        {
289          wchar_t temp[16];
290          ConvertUInt32ToString(opRes, temp);
291          s += L"Error #";
292          s += temp;
293        }
294
295        if (encrypted)
296        {
297          // s += L" : ";
298          // s += LangString(IDS_EXTRACT_MSG_ENCRYPTED);
299          s += L" : ";
300          s += LangString(IDS_EXTRACT_MSG_WRONG_PSW);
301        }
302        s += L" : ";
303        s += _currentFilePath;
304      }
305
306      AddError_Message(s);
307    }
308  }
309
310  #ifndef _SFX
311  if (_isFolder)
312    NumFolders++;
313  else
314    NumFiles++;
315  ProgressDialog->Sync.Set_NumFilesCur(NumFiles);
316  #endif
317
318  return S_OK;
319}
320
321////////////////////////////////////////
322// IExtractCallbackUI
323
324HRESULT CExtractCallbackImp::BeforeOpen(const wchar_t *name)
325{
326  #ifndef _SFX
327  RINOK(ProgressDialog->Sync.CheckStop());
328  ProgressDialog->Sync.Set_TitleFileName(name);
329  #endif
330  _currentArchivePath = name;
331  return S_OK;
332}
333
334HRESULT CExtractCallbackImp::SetCurrentFilePath2(const wchar_t *path)
335{
336  _currentFilePath = path;
337  #ifndef _SFX
338  ProgressDialog->Sync.Set_FilePath(path);
339  #endif
340  return S_OK;
341}
342
343#ifndef _SFX
344
345HRESULT CExtractCallbackImp::SetCurrentFilePath(const wchar_t *path)
346{
347  #ifndef _SFX
348  if (NeedAddFile)
349    NumFiles++;
350  NeedAddFile = true;
351  ProgressDialog->Sync.Set_NumFilesCur(NumFiles);
352  #endif
353  return SetCurrentFilePath2(path);
354}
355
356#endif
357
358UString HResultToMessage(HRESULT errorCode);
359
360HRESULT CExtractCallbackImp::OpenResult(const wchar_t *name, HRESULT result, bool encrypted)
361{
362  if (result != S_OK)
363  {
364    UString s;
365    if (result == S_FALSE)
366      s = MyFormatNew(encrypted ? IDS_CANT_OPEN_ENCRYPTED_ARCHIVE : IDS_CANT_OPEN_ARCHIVE, name);
367    else
368    {
369      s = name;
370      s += L": ";
371      s += HResultToMessage(result);
372    }
373    MessageError(s);
374    NumArchiveErrors++;
375  }
376  _currentArchivePath = name;
377  _needWriteArchivePath = true;
378  return S_OK;
379}
380
381static const UInt32 k_ErrorFlagsIds[] =
382{
383  IDS_EXTRACT_MSG_IS_NOT_ARC,
384  IDS_EXTRACT_MSG_HEADERS_ERROR,
385  IDS_EXTRACT_MSG_HEADERS_ERROR,
386  IDS_OPEN_MSG_UNAVAILABLE_START,
387  IDS_OPEN_MSG_UNCONFIRMED_START,
388  IDS_EXTRACT_MSG_UEXPECTED_END,
389  IDS_EXTRACT_MSG_DATA_AFTER_END,
390  IDS_EXTRACT_MSG_UNSUPPORTED_METHOD,
391  IDS_OPEN_MSG_UNSUPPORTED_FEATURE,
392  IDS_EXTRACT_MSG_DATA_ERROR,
393  IDS_EXTRACT_MSG_CRC_ERROR
394};
395
396UString GetOpenArcErrorMessage(UInt32 errorFlags)
397{
398  UString s;
399  for (unsigned i = 0; i < ARRAY_SIZE(k_ErrorFlagsIds); i++)
400  {
401    UInt32 f = ((UInt32)1 << i);
402    if ((errorFlags & f) == 0)
403      continue;
404    UInt32 id = k_ErrorFlagsIds[i];
405    UString m = LangString(id);
406    if (m.IsEmpty())
407      continue;
408    if (f == kpv_ErrorFlags_EncryptedHeadersError)
409    {
410      m += L" : ";
411      m += LangString(IDS_EXTRACT_MSG_WRONG_PSW);
412    }
413    if (!s.IsEmpty())
414      s += L'\n';
415    s += m;
416    errorFlags &= ~f;
417  }
418  if (errorFlags != 0)
419  {
420    char sz[16];
421    sz[0] = '0';
422    sz[1] = 'x';
423    ConvertUInt32ToHex(errorFlags, sz + 2);
424    if (!s.IsEmpty())
425      s += L'\n';
426    s += GetUnicodeString(AString(sz));
427  }
428  return s;
429}
430
431HRESULT CExtractCallbackImp::SetError(int level, const wchar_t *name,
432    UInt32 errorFlags, const wchar_t *errors,
433    UInt32 warningFlags, const wchar_t *warnings)
434{
435  NumArchiveErrors++;
436
437  if (_needWriteArchivePath)
438  {
439    if (!_currentArchivePath.IsEmpty())
440      AddError_Message(_currentArchivePath);
441    _needWriteArchivePath = false;
442  }
443
444  if (level != 0)
445  {
446    UString s;
447    s += name;
448    s += L": ";
449    MessageError(s);
450  }
451
452  if (errorFlags != 0)
453    MessageError(GetOpenArcErrorMessage(errorFlags));
454
455  if (errors && wcslen(errors) != 0)
456    MessageError(errors);
457
458  if (warningFlags != 0)
459    MessageError((UString)L"Warnings: " + GetOpenArcErrorMessage(warningFlags));
460
461  if (warnings && wcslen(warnings) != 0)
462    MessageError((UString)L"Warnings: " + warnings);
463
464  return S_OK;
465}
466
467HRESULT CExtractCallbackImp::OpenTypeWarning(const wchar_t *name, const wchar_t *okType, const wchar_t *errorType)
468{
469  UString s = L"Warning:\n";
470  s += name;
471  s += L"\n";
472  if (wcscmp(okType, errorType) == 0)
473  {
474    s += L"The archive is open with offset";
475  }
476  else
477  {
478    s += L"Can not open the file as [";
479    s += errorType;
480    s += L"] archive\n";
481    s += L"The file is open as [";
482    s += okType;
483    s += L"] archive";
484  }
485  MessageError(s);
486  NumArchiveErrors++;
487  return S_OK;
488}
489
490HRESULT CExtractCallbackImp::ThereAreNoFiles()
491{
492  return S_OK;
493}
494
495HRESULT CExtractCallbackImp::ExtractResult(HRESULT result)
496{
497  if (result == S_OK)
498    return result;
499  NumArchiveErrors++;
500  if (result == E_ABORT || result == ERROR_DISK_FULL)
501    return result;
502  MessageError(_currentFilePath);
503  MessageError(NError::MyFormatMessage(result));
504  return S_OK;
505}
506
507#ifndef _NO_CRYPTO
508
509HRESULT CExtractCallbackImp::SetPassword(const UString &password)
510{
511  PasswordIsDefined = true;
512  Password = password;
513  return S_OK;
514}
515
516STDMETHODIMP CExtractCallbackImp::CryptoGetTextPassword(BSTR *password)
517{
518  PasswordWasAsked = true;
519  if (!PasswordIsDefined)
520  {
521    CPasswordDialog dialog;
522    #ifndef _SFX
523    bool showPassword = NExtract::Read_ShowPassword();
524    dialog.ShowPassword = showPassword;
525    #endif
526    ProgressDialog->WaitCreating();
527    if (dialog.Create(*ProgressDialog) != IDOK)
528      return E_ABORT;
529    Password = dialog.Password;
530    PasswordIsDefined = true;
531    #ifndef _SFX
532    if (dialog.ShowPassword != showPassword)
533      NExtract::Save_ShowPassword(dialog.ShowPassword);
534    #endif
535  }
536  return StringToBstr(Password, password);
537}
538
539#endif
540
541#ifndef _SFX
542
543STDMETHODIMP CExtractCallbackImp::AskWrite(
544    const wchar_t *srcPath, Int32 srcIsFolder,
545    const FILETIME *srcTime, const UInt64 *srcSize,
546    const wchar_t *destPath,
547    BSTR *destPathResult,
548    Int32 *writeAnswer)
549{
550  UString destPathResultTemp = destPath;
551
552  // RINOK(StringToBstr(destPath, destPathResult));
553
554  *destPathResult = 0;
555  *writeAnswer = BoolToInt(false);
556
557  FString destPathSys = us2fs(destPath);
558  bool srcIsFolderSpec = IntToBool(srcIsFolder);
559  CFileInfo destFileInfo;
560
561  if (destFileInfo.Find(destPathSys))
562  {
563    if (srcIsFolderSpec)
564    {
565      if (!destFileInfo.IsDir())
566      {
567        RINOK(MessageError("can not replace file with folder with same name: ", destPathSys));
568        return E_ABORT;
569      }
570      *writeAnswer = BoolToInt(false);
571      return S_OK;
572    }
573
574    if (destFileInfo.IsDir())
575    {
576      RINOK(MessageError("can not replace folder with file with same name: ", destPathSys));
577      return E_FAIL;
578    }
579
580    switch (OverwriteMode)
581    {
582      case NExtract::NOverwriteMode::kSkip:
583        return S_OK;
584      case NExtract::NOverwriteMode::kAsk:
585      {
586        Int32 overwiteResult;
587        UString destPathSpec = destPath;
588        int slashPos = destPathSpec.ReverseFind(L'/');
589        #ifdef _WIN32
590        int slash1Pos = destPathSpec.ReverseFind(L'\\');
591        slashPos = MyMax(slashPos, slash1Pos);
592        #endif
593        destPathSpec.DeleteFrom(slashPos + 1);
594        destPathSpec += fs2us(destFileInfo.Name);
595
596        RINOK(AskOverwrite(
597            destPathSpec,
598            &destFileInfo.MTime, &destFileInfo.Size,
599            srcPath,
600            srcTime, srcSize,
601            &overwiteResult));
602
603        switch (overwiteResult)
604        {
605          case NOverwriteAnswer::kCancel: return E_ABORT;
606          case NOverwriteAnswer::kNo: return S_OK;
607          case NOverwriteAnswer::kNoToAll: OverwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK;
608          case NOverwriteAnswer::kYes: break;
609          case NOverwriteAnswer::kYesToAll: OverwriteMode = NExtract::NOverwriteMode::kOverwrite; break;
610          case NOverwriteAnswer::kAutoRename: OverwriteMode = NExtract::NOverwriteMode::kRename; break;
611          default:
612            return E_FAIL;
613        }
614      }
615    }
616
617    if (OverwriteMode == NExtract::NOverwriteMode::kRename)
618    {
619      if (!AutoRenamePath(destPathSys))
620      {
621        RINOK(MessageError("can not create name for file: ", destPathSys));
622        return E_ABORT;
623      }
624      destPathResultTemp = fs2us(destPathSys);
625    }
626    else
627      if (!NDir::DeleteFileAlways(destPathSys))
628      {
629        RINOK(MessageError("can not delete output file: ", destPathSys));
630        return E_ABORT;
631      }
632  }
633  *writeAnswer = BoolToInt(true);
634  return StringToBstr(destPathResultTemp, destPathResult);
635}
636
637
638STDMETHODIMP CExtractCallbackImp::UseExtractToStream(Int32 *res)
639{
640  *res = BoolToInt(StreamMode);
641  return S_OK;
642}
643
644static HRESULT GetTime(IGetProp *getProp, PROPID propID, FILETIME &ft, bool &ftDefined)
645{
646  ftDefined = false;
647  NCOM::CPropVariant prop;
648  RINOK(getProp->GetProp(propID, &prop));
649  if (prop.vt == VT_FILETIME)
650  {
651    ft = prop.filetime;
652    ftDefined = (ft.dwHighDateTime != 0 || ft.dwLowDateTime != 0);
653  }
654  else if (prop.vt != VT_EMPTY)
655    return E_FAIL;
656  return S_OK;
657}
658
659
660static HRESULT GetItemBoolProp(IGetProp *getProp, PROPID propID, bool &result)
661{
662  NCOM::CPropVariant prop;
663  result = false;
664  RINOK(getProp->GetProp(propID, &prop));
665  if (prop.vt == VT_BOOL)
666    result = VARIANT_BOOLToBool(prop.boolVal);
667  else if (prop.vt != VT_EMPTY)
668    return E_FAIL;
669  return S_OK;
670}
671
672
673STDMETHODIMP CExtractCallbackImp::GetStream7(const wchar_t *name,
674    Int32 isDir,
675    ISequentialOutStream **outStream, Int32 askExtractMode,
676    IGetProp *getProp)
677{
678  COM_TRY_BEGIN
679  *outStream = 0;
680  _newVirtFileWasAdded = false;
681  _hashStreamWasUsed = false;
682  _needUpdateStat = false;
683
684  if (_hashStream)
685    _hashStreamSpec->ReleaseStream();
686
687  GetItemBoolProp(getProp, kpidIsAltStream, _isAltStream);
688
689  if (!ProcessAltStreams && _isAltStream)
690    return S_OK;
691
692  _filePath = name;
693  _isFolder = IntToBool(isDir);
694  _curSize = 0;
695  _curSizeDefined = false;
696
697  UInt64 size = 0;
698  bool sizeDefined;
699  {
700    NCOM::CPropVariant prop;
701    RINOK(getProp->GetProp(kpidSize, &prop));
702    sizeDefined = ConvertPropVariantToUInt64(prop, size);
703  }
704
705  if (sizeDefined)
706  {
707    _curSize = size;
708    _curSizeDefined = true;
709  }
710
711  if (askExtractMode != NArchive::NExtract::NAskMode::kExtract &&
712      askExtractMode != NArchive::NExtract::NAskMode::kTest)
713    return S_OK;
714
715  _needUpdateStat = true;
716
717  CMyComPtr<ISequentialOutStream> outStreamLoc;
718
719  if (VirtFileSystem && askExtractMode == NArchive::NExtract::NAskMode::kExtract)
720  {
721    CVirtFile &file = VirtFileSystemSpec->AddNewFile();
722    _newVirtFileWasAdded = true;
723    file.Name = name;
724    file.IsDir = IntToBool(isDir);
725    file.IsAltStream = _isAltStream;
726    file.Size = 0;
727
728    RINOK(GetTime(getProp, kpidCTime, file.CTime, file.CTimeDefined));
729    RINOK(GetTime(getProp, kpidATime, file.ATime, file.ATimeDefined));
730    RINOK(GetTime(getProp, kpidMTime, file.MTime, file.MTimeDefined));
731
732    NCOM::CPropVariant prop;
733    RINOK(getProp->GetProp(kpidAttrib, &prop));
734    if (prop.vt == VT_UI4)
735    {
736      file.Attrib = prop.ulVal;
737      file.AttribDefined = true;
738    }
739    // else if (isDir) file.Attrib = FILE_ATTRIBUTE_DIRECTORY;
740
741    file.ExpectedSize = 0;
742    if (sizeDefined)
743      file.ExpectedSize = size;
744    outStreamLoc = VirtFileSystem;
745  }
746
747  if (_hashStream)
748  {
749    {
750      _hashStreamSpec->SetStream(outStreamLoc);
751      outStreamLoc = _hashStream;
752      _hashStreamSpec->Init(true);
753      _hashStreamWasUsed = true;
754    }
755  }
756
757  if (outStreamLoc)
758    *outStream = outStreamLoc.Detach();
759  return S_OK;
760  COM_TRY_END
761}
762
763STDMETHODIMP CExtractCallbackImp::PrepareOperation7(Int32 askExtractMode)
764{
765  COM_TRY_BEGIN
766  _needUpdateStat = (
767      askExtractMode == NArchive::NExtract::NAskMode::kExtract ||
768      askExtractMode == NArchive::NExtract::NAskMode::kTest);
769
770  /*
771  _extractMode = false;
772  switch (askExtractMode)
773  {
774    case NArchive::NExtract::NAskMode::kExtract:
775      if (_testMode)
776        askExtractMode = NArchive::NExtract::NAskMode::kTest;
777      else
778        _extractMode = true;
779      break;
780  };
781  */
782  return SetCurrentFilePath2(_filePath);
783  COM_TRY_END
784}
785
786STDMETHODIMP CExtractCallbackImp::SetOperationResult7(Int32 opRes, bool encrypted)
787{
788  COM_TRY_BEGIN
789  if (VirtFileSystem && _newVirtFileWasAdded)
790  {
791    // FIXME: probably we must request file size from VirtFileSystem
792    // _curSize = VirtFileSystem->GetLastFileSize()
793    // _curSizeDefined = true;
794    RINOK(VirtFileSystemSpec->CloseMemFile());
795  }
796  if (_hashStream && _hashStreamWasUsed)
797  {
798    _hashStreamSpec->_hash->Final(_isFolder, _isAltStream, _filePath);
799    _curSize = _hashStreamSpec->GetSize();
800    _curSizeDefined = true;
801    _hashStreamSpec->ReleaseStream();
802    _hashStreamWasUsed = false;
803  }
804  else if (_hashCalc && _needUpdateStat)
805  {
806    _hashCalc->SetSize(_curSize);
807    _hashCalc->Final(_isFolder, _isAltStream, _filePath);
808  }
809  return SetOperationResult(opRes, encrypted);
810  COM_TRY_END
811}
812
813
814static const size_t k_SizeT_MAX = (size_t)((size_t)0 - 1);
815
816static const UInt32 kBlockSize = ((UInt32)1 << 31);
817
818STDMETHODIMP CVirtFileSystem::Write(const void *data, UInt32 size, UInt32 *processedSize)
819{
820  if (processedSize)
821    *processedSize = 0;
822  if (size == 0)
823    return S_OK;
824  if (!_fileMode)
825  {
826    CVirtFile &file = Files.Back();
827    size_t rem = file.Data.Size() - (size_t)file.Size;
828    bool useMem = true;
829    if (rem < size)
830    {
831      UInt64 b = 0;
832      if (file.Data.Size() == 0)
833        b = file.ExpectedSize;
834      UInt64 a = file.Size + size;
835      if (b < a)
836        b = a;
837      a = (UInt64)file.Data.Size() * 2;
838      if (b < a)
839        b = a;
840      useMem = false;
841      if (b <= k_SizeT_MAX && b <= MaxTotalAllocSize)
842        useMem = file.Data.ReAlloc_KeepData((size_t)b, (size_t)file.Size);
843    }
844    if (useMem)
845    {
846      memcpy(file.Data + file.Size, data, size);
847      file.Size += size;
848      if (processedSize)
849        *processedSize = (UInt32)size;
850      return S_OK;
851    }
852    _fileMode = true;
853  }
854  RINOK(FlushToDisk(false));
855  return _outFileStream->Write(data, size, processedSize);
856}
857
858HRESULT CVirtFileSystem::FlushToDisk(bool closeLast)
859{
860  if (!_outFileStream)
861  {
862    _outFileStreamSpec = new COutFileStream;
863    _outFileStream = _outFileStreamSpec;
864  }
865  while (_numFlushed < Files.Size())
866  {
867    const CVirtFile &file = Files[_numFlushed];
868    const FString path = DirPrefix + us2fs(GetCorrectFsPath(file.Name));
869    if (!_fileIsOpen)
870    {
871      if (!_outFileStreamSpec->Create(path, false))
872      {
873        _outFileStream.Release();
874        return E_FAIL;
875        // MessageBoxMyError(UString(L"Can't create file ") + fs2us(tempFilePath));
876      }
877      _fileIsOpen = true;
878      RINOK(WriteStream(_outFileStream, file.Data, (size_t)file.Size));
879    }
880    if (_numFlushed == Files.Size() - 1 && !closeLast)
881      break;
882    if (file.CTimeDefined ||
883        file.ATimeDefined ||
884        file.MTimeDefined)
885      _outFileStreamSpec->SetTime(
886          file.CTimeDefined ? &file.CTime : NULL,
887          file.ATimeDefined ? &file.ATime : NULL,
888          file.MTimeDefined ? &file.MTime : NULL);
889    _outFileStreamSpec->Close();
890    _numFlushed++;
891    _fileIsOpen = false;
892    if (file.AttribDefined)
893      NDir::SetFileAttrib(path, file.Attrib);
894  }
895  return S_OK;
896}
897
898#endif
899