Home | History | Annotate | Download | only in FileManager
      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 
     34 using namespace NWindows;
     35 using namespace NFile;
     36 using namespace NFind;
     37 
     38 CExtractCallbackImp::~CExtractCallbackImp() {}
     39 
     40 void CExtractCallbackImp::Init()
     41 {
     42   NumArchiveErrors = 0;
     43   ThereAreMessageErrors = false;
     44   #ifndef _SFX
     45   NumFolders = NumFiles = 0;
     46   NeedAddFile = false;
     47   #endif
     48 }
     49 
     50 void CExtractCallbackImp::AddError_Message(LPCWSTR s)
     51 {
     52   ThereAreMessageErrors = true;
     53   ProgressDialog->Sync.AddError_Message(s);
     54 }
     55 
     56 #ifndef _SFX
     57 
     58 STDMETHODIMP 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 
     72 STDMETHODIMP CExtractCallbackImp::SetTotal(UInt64 total)
     73 {
     74   ProgressDialog->Sync.Set_NumBytesTotal(total);
     75   return S_OK;
     76 }
     77 
     78 STDMETHODIMP CExtractCallbackImp::SetCompleted(const UInt64 *value)
     79 {
     80   return ProgressDialog->Sync.Set_NumBytesCur(value);
     81 }
     82 
     83 HRESULT CExtractCallbackImp::Open_CheckBreak()
     84 {
     85   return ProgressDialog->Sync.CheckStop();
     86 }
     87 
     88 HRESULT CExtractCallbackImp::Open_SetTotal(const UInt64 * /* numFiles */, const UInt64 * /* numBytes */)
     89 {
     90   // if (numFiles != NULL) ProgressDialog->Sync.SetNumFilesTotal(*numFiles);
     91   return S_OK;
     92 }
     93 
     94 HRESULT 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 
    102 HRESULT CExtractCallbackImp::Open_CryptoGetTextPassword(BSTR *password)
    103 {
    104   return CryptoGetTextPassword(password);
    105 }
    106 
    107 HRESULT CExtractCallbackImp::Open_GetPasswordIfAny(bool &passwordIsDefined, UString &password)
    108 {
    109   passwordIsDefined = PasswordIsDefined;
    110   password = Password;
    111   return S_OK;
    112 }
    113 
    114 bool CExtractCallbackImp::Open_WasPasswordAsked()
    115 {
    116   return PasswordWasAsked;
    117 }
    118 
    119 void CExtractCallbackImp::Open_ClearPasswordWasAskedFlag()
    120 {
    121   PasswordWasAsked = false;
    122 }
    123 
    124 #endif
    125 
    126 
    127 #ifndef _SFX
    128 STDMETHODIMP 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 /*
    136 STDMETHODIMP CExtractCallbackImp::SetTotalFiles(UInt64 total)
    137 {
    138   ProgressDialog->Sync.SetNumFilesTotal(total);
    139   return S_OK;
    140 }
    141 
    142 STDMETHODIMP CExtractCallbackImp::SetCompletedFiles(const UInt64 *value)
    143 {
    144   if (value != NULL)
    145     ProgressDialog->Sync.SetNumFilesCur(*value);
    146   return S_OK;
    147 }
    148 */
    149 
    150 STDMETHODIMP 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 
    182 STDMETHODIMP CExtractCallbackImp::PrepareOperation(const wchar_t *name, bool isFolder, Int32 /* askExtractMode */, const UInt64 * /* position */)
    183 {
    184   _isFolder = isFolder;
    185   return SetCurrentFilePath2(name);
    186 }
    187 
    188 STDMETHODIMP CExtractCallbackImp::MessageError(const wchar_t *s)
    189 {
    190   AddError_Message(s);
    191   return S_OK;
    192 }
    193 
    194 HRESULT 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 
    203 STDMETHODIMP CExtractCallbackImp::ShowMessage(const wchar_t *s)
    204 {
    205   AddError_Message(s);
    206   return S_OK;
    207 }
    208 
    209 #endif
    210 
    211 STDMETHODIMP 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 
    324 HRESULT 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 
    334 HRESULT 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 
    345 HRESULT 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 
    358 UString HResultToMessage(HRESULT errorCode);
    359 
    360 HRESULT 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 
    381 static 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 
    396 UString 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 
    431 HRESULT 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 
    467 HRESULT 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 
    490 HRESULT CExtractCallbackImp::ThereAreNoFiles()
    491 {
    492   return S_OK;
    493 }
    494 
    495 HRESULT 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 
    509 HRESULT CExtractCallbackImp::SetPassword(const UString &password)
    510 {
    511   PasswordIsDefined = true;
    512   Password = password;
    513   return S_OK;
    514 }
    515 
    516 STDMETHODIMP 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 
    543 STDMETHODIMP 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 
    638 STDMETHODIMP CExtractCallbackImp::UseExtractToStream(Int32 *res)
    639 {
    640   *res = BoolToInt(StreamMode);
    641   return S_OK;
    642 }
    643 
    644 static 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 
    660 static 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 
    673 STDMETHODIMP 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 
    763 STDMETHODIMP 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 
    786 STDMETHODIMP 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 
    814 static const size_t k_SizeT_MAX = (size_t)((size_t)0 - 1);
    815 
    816 static const UInt32 kBlockSize = ((UInt32)1 << 31);
    817 
    818 STDMETHODIMP 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 
    858 HRESULT 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