// ExtractCallback.cpp
#include "StdAfx.h"
#include "../../../Common/ComTry.h"
#include "../../../Common/IntToString.h"
#include "../../../Common/Lang.h"
#include "../../../Common/StringConvert.h"
#include "../../../Windows/ErrorMsg.h"
#include "../../../Windows/FileDir.h"
#include "../../../Windows/FileFind.h"
#include "../../../Windows/PropVariantConv.h"
#include "../../Common/FilePathAutoRename.h"
#include "../../Common/StreamUtils.h"
#include "../Common/ExtractingFilePath.h"
#ifndef _SFX
#include "../Common/ZipRegistry.h"
#endif
#include "../GUI/ExtractRes.h"
#include "ExtractCallback.h"
#include "FormatUtils.h"
#include "LangUtils.h"
#include "OverwriteDialog.h"
#ifndef _NO_CRYPTO
#include "PasswordDialog.h"
#endif
using namespace NWindows;
using namespace NFile;
using namespace NFind;
CExtractCallbackImp::~CExtractCallbackImp() {}
void CExtractCallbackImp::Init()
{
NumArchiveErrors = 0;
ThereAreMessageErrors = false;
#ifndef _SFX
NumFolders = NumFiles = 0;
NeedAddFile = false;
#endif
}
void CExtractCallbackImp::AddError_Message(LPCWSTR s)
{
ThereAreMessageErrors = true;
ProgressDialog->Sync.AddError_Message(s);
}
#ifndef _SFX
STDMETHODIMP CExtractCallbackImp::SetNumFiles(UInt64
#ifndef _SFX
numFiles
#endif
)
{
#ifndef _SFX
ProgressDialog->Sync.Set_NumFilesTotal(numFiles);
#endif
return S_OK;
}
#endif
STDMETHODIMP CExtractCallbackImp::SetTotal(UInt64 total)
{
ProgressDialog->Sync.Set_NumBytesTotal(total);
return S_OK;
}
STDMETHODIMP CExtractCallbackImp::SetCompleted(const UInt64 *value)
{
return ProgressDialog->Sync.Set_NumBytesCur(value);
}
HRESULT CExtractCallbackImp::Open_CheckBreak()
{
return ProgressDialog->Sync.CheckStop();
}
HRESULT CExtractCallbackImp::Open_SetTotal(const UInt64 * /* numFiles */, const UInt64 * /* numBytes */)
{
// if (numFiles != NULL) ProgressDialog->Sync.SetNumFilesTotal(*numFiles);
return S_OK;
}
HRESULT CExtractCallbackImp::Open_SetCompleted(const UInt64 * /* numFiles */, const UInt64 * /* numBytes */)
{
// if (numFiles != NULL) ProgressDialog->Sync.SetNumFilesCur(*numFiles);
return ProgressDialog->Sync.CheckStop();
}
#ifndef _NO_CRYPTO
HRESULT CExtractCallbackImp::Open_CryptoGetTextPassword(BSTR *password)
{
return CryptoGetTextPassword(password);
}
HRESULT CExtractCallbackImp::Open_GetPasswordIfAny(bool &passwordIsDefined, UString &password)
{
passwordIsDefined = PasswordIsDefined;
password = Password;
return S_OK;
}
bool CExtractCallbackImp::Open_WasPasswordAsked()
{
return PasswordWasAsked;
}
void CExtractCallbackImp::Open_ClearPasswordWasAskedFlag()
{
PasswordWasAsked = false;
}
#endif
#ifndef _SFX
STDMETHODIMP CExtractCallbackImp::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
{
ProgressDialog->Sync.Set_Ratio(inSize, outSize);
return S_OK;
}
#endif
/*
STDMETHODIMP CExtractCallbackImp::SetTotalFiles(UInt64 total)
{
ProgressDialog->Sync.SetNumFilesTotal(total);
return S_OK;
}
STDMETHODIMP CExtractCallbackImp::SetCompletedFiles(const UInt64 *value)
{
if (value != NULL)
ProgressDialog->Sync.SetNumFilesCur(*value);
return S_OK;
}
*/
STDMETHODIMP CExtractCallbackImp::AskOverwrite(
const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize,
const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize,
Int32 *answer)
{
COverwriteDialog dialog;
dialog.OldFileInfo.SetTime(existTime);
dialog.OldFileInfo.SetSize(existSize);
dialog.OldFileInfo.Name = existName;
dialog.NewFileInfo.SetTime(newTime);
dialog.NewFileInfo.SetSize(newSize);
dialog.NewFileInfo.Name = newName;
ProgressDialog->WaitCreating();
INT_PTR writeAnswer = dialog.Create(*ProgressDialog);
switch (writeAnswer)
{
case IDCANCEL: *answer = NOverwriteAnswer::kCancel; return E_ABORT;
case IDYES: *answer = NOverwriteAnswer::kYes; break;
case IDNO: *answer = NOverwriteAnswer::kNo; break;
case IDB_YES_TO_ALL: *answer = NOverwriteAnswer::kYesToAll; break;
case IDB_NO_TO_ALL: *answer = NOverwriteAnswer::kNoToAll; break;
case IDB_AUTO_RENAME: *answer = NOverwriteAnswer::kAutoRename; break;
default: return E_FAIL;
}
return S_OK;
}
STDMETHODIMP CExtractCallbackImp::PrepareOperation(const wchar_t *name, bool isFolder, Int32 /* askExtractMode */, const UInt64 * /* position */)
{
_isFolder = isFolder;
return SetCurrentFilePath2(name);
}
STDMETHODIMP CExtractCallbackImp::MessageError(const wchar_t *s)
{
AddError_Message(s);
return S_OK;
}
HRESULT CExtractCallbackImp::MessageError(const char *message, const FString &path)
{
ThereAreMessageErrors = true;
ProgressDialog->Sync.AddError_Message_Name(GetUnicodeString(message), fs2us(path));
return S_OK;
}
#ifndef _SFX
STDMETHODIMP CExtractCallbackImp::ShowMessage(const wchar_t *s)
{
AddError_Message(s);
return S_OK;
}
#endif
STDMETHODIMP CExtractCallbackImp::SetOperationResult(Int32 opRes, bool encrypted)
{
switch (opRes)
{
case NArchive::NExtract::NOperationResult::kOK:
break;
default:
{
UINT messageID = 0;
UINT id = 0;
switch (opRes)
{
case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
messageID = IDS_EXTRACT_MESSAGE_UNSUPPORTED_METHOD;
id = IDS_EXTRACT_MSG_UNSUPPORTED_METHOD;
break;
case NArchive::NExtract::NOperationResult::kDataError:
messageID = encrypted ?
IDS_EXTRACT_MESSAGE_DATA_ERROR_ENCRYPTED:
IDS_EXTRACT_MESSAGE_DATA_ERROR;
id = IDS_EXTRACT_MSG_DATA_ERROR;
break;
case NArchive::NExtract::NOperationResult::kCRCError:
messageID = encrypted ?
IDS_EXTRACT_MESSAGE_CRC_ERROR_ENCRYPTED:
IDS_EXTRACT_MESSAGE_CRC_ERROR;
id = IDS_EXTRACT_MSG_CRC_ERROR;
break;
case NArchive::NExtract::NOperationResult::kUnavailable:
id = IDS_EXTRACT_MSG_UNAVAILABLE_DATA;
break;
case NArchive::NExtract::NOperationResult::kUnexpectedEnd:
id = IDS_EXTRACT_MSG_UEXPECTED_END;
break;
case NArchive::NExtract::NOperationResult::kDataAfterEnd:
id = IDS_EXTRACT_MSG_DATA_AFTER_END;
break;
case NArchive::NExtract::NOperationResult::kIsNotArc:
id = IDS_EXTRACT_MSG_IS_NOT_ARC;
break;
case NArchive::NExtract::NOperationResult::kHeadersError:
id = IDS_EXTRACT_MSG_HEADERS_ERROR;
break;
/*
default:
messageID = IDS_EXTRACT_MESSAGE_UNKNOWN_ERROR;
break;
*/
}
if (_needWriteArchivePath)
{
if (!_currentArchivePath.IsEmpty())
AddError_Message(_currentArchivePath);
_needWriteArchivePath = false;
}
UString msg;
UString msgOld;
#ifndef _SFX
if (id != 0)
LangString_OnlyFromLangFile(id, msg);
if (messageID != 0 && msg.IsEmpty())
LangString_OnlyFromLangFile(messageID, msgOld);
#endif
UString s;
if (msg.IsEmpty() && !msgOld.IsEmpty())
s = MyFormatNew(msgOld, _currentFilePath);
else
{
if (msg.IsEmpty())
LangString(id, msg);
if (!msg.IsEmpty())
s += msg;
else
{
wchar_t temp[16];
ConvertUInt32ToString(opRes, temp);
s += L"Error #";
s += temp;
}
if (encrypted)
{
// s += L" : ";
// s += LangString(IDS_EXTRACT_MSG_ENCRYPTED);
s += L" : ";
s += LangString(IDS_EXTRACT_MSG_WRONG_PSW);
}
s += L" : ";
s += _currentFilePath;
}
AddError_Message(s);
}
}
#ifndef _SFX
if (_isFolder)
NumFolders++;
else
NumFiles++;
ProgressDialog->Sync.Set_NumFilesCur(NumFiles);
#endif
return S_OK;
}
////////////////////////////////////////
// IExtractCallbackUI
HRESULT CExtractCallbackImp::BeforeOpen(const wchar_t *name)
{
#ifndef _SFX
RINOK(ProgressDialog->Sync.CheckStop());
ProgressDialog->Sync.Set_TitleFileName(name);
#endif
_currentArchivePath = name;
return S_OK;
}
HRESULT CExtractCallbackImp::SetCurrentFilePath2(const wchar_t *path)
{
_currentFilePath = path;
#ifndef _SFX
ProgressDialog->Sync.Set_FilePath(path);
#endif
return S_OK;
}
#ifndef _SFX
HRESULT CExtractCallbackImp::SetCurrentFilePath(const wchar_t *path)
{
#ifndef _SFX
if (NeedAddFile)
NumFiles++;
NeedAddFile = true;
ProgressDialog->Sync.Set_NumFilesCur(NumFiles);
#endif
return SetCurrentFilePath2(path);
}
#endif
UString HResultToMessage(HRESULT errorCode);
HRESULT CExtractCallbackImp::OpenResult(const wchar_t *name, HRESULT result, bool encrypted)
{
if (result != S_OK)
{
UString s;
if (result == S_FALSE)
s = MyFormatNew(encrypted ? IDS_CANT_OPEN_ENCRYPTED_ARCHIVE : IDS_CANT_OPEN_ARCHIVE, name);
else
{
s = name;
s += L": ";
s += HResultToMessage(result);
}
MessageError(s);
NumArchiveErrors++;
}
_currentArchivePath = name;
_needWriteArchivePath = true;
return S_OK;
}
static const UInt32 k_ErrorFlagsIds[] =
{
IDS_EXTRACT_MSG_IS_NOT_ARC,
IDS_EXTRACT_MSG_HEADERS_ERROR,
IDS_EXTRACT_MSG_HEADERS_ERROR,
IDS_OPEN_MSG_UNAVAILABLE_START,
IDS_OPEN_MSG_UNCONFIRMED_START,
IDS_EXTRACT_MSG_UEXPECTED_END,
IDS_EXTRACT_MSG_DATA_AFTER_END,
IDS_EXTRACT_MSG_UNSUPPORTED_METHOD,
IDS_OPEN_MSG_UNSUPPORTED_FEATURE,
IDS_EXTRACT_MSG_DATA_ERROR,
IDS_EXTRACT_MSG_CRC_ERROR
};
UString GetOpenArcErrorMessage(UInt32 errorFlags)
{
UString s;
for (unsigned i = 0; i < ARRAY_SIZE(k_ErrorFlagsIds); i++)
{
UInt32 f = ((UInt32)1 << i);
if ((errorFlags & f) == 0)
continue;
UInt32 id = k_ErrorFlagsIds[i];
UString m = LangString(id);
if (m.IsEmpty())
continue;
if (f == kpv_ErrorFlags_EncryptedHeadersError)
{
m += L" : ";
m += LangString(IDS_EXTRACT_MSG_WRONG_PSW);
}
if (!s.IsEmpty())
s += L'\n';
s += m;
errorFlags &= ~f;
}
if (errorFlags != 0)
{
char sz[16];
sz[0] = '0';
sz[1] = 'x';
ConvertUInt32ToHex(errorFlags, sz + 2);
if (!s.IsEmpty())
s += L'\n';
s += GetUnicodeString(AString(sz));
}
return s;
}
HRESULT CExtractCallbackImp::SetError(int level, const wchar_t *name,
UInt32 errorFlags, const wchar_t *errors,
UInt32 warningFlags, const wchar_t *warnings)
{
NumArchiveErrors++;
if (_needWriteArchivePath)
{
if (!_currentArchivePath.IsEmpty())
AddError_Message(_currentArchivePath);
_needWriteArchivePath = false;
}
if (level != 0)
{
UString s;
s += name;
s += L": ";
MessageError(s);
}
if (errorFlags != 0)
MessageError(GetOpenArcErrorMessage(errorFlags));
if (errors && wcslen(errors) != 0)
MessageError(errors);
if (warningFlags != 0)
MessageError((UString)L"Warnings: " + GetOpenArcErrorMessage(warningFlags));
if (warnings && wcslen(warnings) != 0)
MessageError((UString)L"Warnings: " + warnings);
return S_OK;
}
HRESULT CExtractCallbackImp::OpenTypeWarning(const wchar_t *name, const wchar_t *okType, const wchar_t *errorType)
{
UString s = L"Warning:\n";
s += name;
s += L"\n";
if (wcscmp(okType, errorType) == 0)
{
s += L"The archive is open with offset";
}
else
{
s += L"Can not open the file as [";
s += errorType;
s += L"] archive\n";
s += L"The file is open as [";
s += okType;
s += L"] archive";
}
MessageError(s);
NumArchiveErrors++;
return S_OK;
}
HRESULT CExtractCallbackImp::ThereAreNoFiles()
{
return S_OK;
}
HRESULT CExtractCallbackImp::ExtractResult(HRESULT result)
{
if (result == S_OK)
return result;
NumArchiveErrors++;
if (result == E_ABORT || result == ERROR_DISK_FULL)
return result;
MessageError(_currentFilePath);
MessageError(NError::MyFormatMessage(result));
return S_OK;
}
#ifndef _NO_CRYPTO
HRESULT CExtractCallbackImp::SetPassword(const UString &password)
{
PasswordIsDefined = true;
Password = password;
return S_OK;
}
STDMETHODIMP CExtractCallbackImp::CryptoGetTextPassword(BSTR *password)
{
PasswordWasAsked = true;
if (!PasswordIsDefined)
{
CPasswordDialog dialog;
#ifndef _SFX
bool showPassword = NExtract::Read_ShowPassword();
dialog.ShowPassword = showPassword;
#endif
ProgressDialog->WaitCreating();
if (dialog.Create(*ProgressDialog) != IDOK)
return E_ABORT;
Password = dialog.Password;
PasswordIsDefined = true;
#ifndef _SFX
if (dialog.ShowPassword != showPassword)
NExtract::Save_ShowPassword(dialog.ShowPassword);
#endif
}
return StringToBstr(Password, password);
}
#endif
#ifndef _SFX
STDMETHODIMP CExtractCallbackImp::AskWrite(
const wchar_t *srcPath, Int32 srcIsFolder,
const FILETIME *srcTime, const UInt64 *srcSize,
const wchar_t *destPath,
BSTR *destPathResult,
Int32 *writeAnswer)
{
UString destPathResultTemp = destPath;
// RINOK(StringToBstr(destPath, destPathResult));
*destPathResult = 0;
*writeAnswer = BoolToInt(false);
FString destPathSys = us2fs(destPath);
bool srcIsFolderSpec = IntToBool(srcIsFolder);
CFileInfo destFileInfo;
if (destFileInfo.Find(destPathSys))
{
if (srcIsFolderSpec)
{
if (!destFileInfo.IsDir())
{
RINOK(MessageError("can not replace file with folder with same name: ", destPathSys));
return E_ABORT;
}
*writeAnswer = BoolToInt(false);
return S_OK;
}
if (destFileInfo.IsDir())
{
RINOK(MessageError("can not replace folder with file with same name: ", destPathSys));
return E_FAIL;
}
switch (OverwriteMode)
{
case NExtract::NOverwriteMode::kSkip:
return S_OK;
case NExtract::NOverwriteMode::kAsk:
{
Int32 overwiteResult;
UString destPathSpec = destPath;
int slashPos = destPathSpec.ReverseFind(L'/');
#ifdef _WIN32
int slash1Pos = destPathSpec.ReverseFind(L'\\');
slashPos = MyMax(slashPos, slash1Pos);
#endif
destPathSpec.DeleteFrom(slashPos + 1);
destPathSpec += fs2us(destFileInfo.Name);
RINOK(AskOverwrite(
destPathSpec,
&destFileInfo.MTime, &destFileInfo.Size,
srcPath,
srcTime, srcSize,
&overwiteResult));
switch (overwiteResult)
{
case NOverwriteAnswer::kCancel: return E_ABORT;
case NOverwriteAnswer::kNo: return S_OK;
case NOverwriteAnswer::kNoToAll: OverwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK;
case NOverwriteAnswer::kYes: break;
case NOverwriteAnswer::kYesToAll: OverwriteMode = NExtract::NOverwriteMode::kOverwrite; break;
case NOverwriteAnswer::kAutoRename: OverwriteMode = NExtract::NOverwriteMode::kRename; break;
default:
return E_FAIL;
}
}
}
if (OverwriteMode == NExtract::NOverwriteMode::kRename)
{
if (!AutoRenamePath(destPathSys))
{
RINOK(MessageError("can not create name for file: ", destPathSys));
return E_ABORT;
}
destPathResultTemp = fs2us(destPathSys);
}
else
if (!NDir::DeleteFileAlways(destPathSys))
{
RINOK(MessageError("can not delete output file: ", destPathSys));
return E_ABORT;
}
}
*writeAnswer = BoolToInt(true);
return StringToBstr(destPathResultTemp, destPathResult);
}
STDMETHODIMP CExtractCallbackImp::UseExtractToStream(Int32 *res)
{
*res = BoolToInt(StreamMode);
return S_OK;
}
static HRESULT GetTime(IGetProp *getProp, PROPID propID, FILETIME &ft, bool &ftDefined)
{
ftDefined = false;
NCOM::CPropVariant prop;
RINOK(getProp->GetProp(propID, &prop));
if (prop.vt == VT_FILETIME)
{
ft = prop.filetime;
ftDefined = (ft.dwHighDateTime != 0 || ft.dwLowDateTime != 0);
}
else if (prop.vt != VT_EMPTY)
return E_FAIL;
return S_OK;
}
static HRESULT GetItemBoolProp(IGetProp *getProp, PROPID propID, bool &result)
{
NCOM::CPropVariant prop;
result = false;
RINOK(getProp->GetProp(propID, &prop));
if (prop.vt == VT_BOOL)
result = VARIANT_BOOLToBool(prop.boolVal);
else if (prop.vt != VT_EMPTY)
return E_FAIL;
return S_OK;
}
STDMETHODIMP CExtractCallbackImp::GetStream7(const wchar_t *name,
Int32 isDir,
ISequentialOutStream **outStream, Int32 askExtractMode,
IGetProp *getProp)
{
COM_TRY_BEGIN
*outStream = 0;
_newVirtFileWasAdded = false;
_hashStreamWasUsed = false;
_needUpdateStat = false;
if (_hashStream)
_hashStreamSpec->ReleaseStream();
GetItemBoolProp(getProp, kpidIsAltStream, _isAltStream);
if (!ProcessAltStreams && _isAltStream)
return S_OK;
_filePath = name;
_isFolder = IntToBool(isDir);
_curSize = 0;
_curSizeDefined = false;
UInt64 size = 0;
bool sizeDefined;
{
NCOM::CPropVariant prop;
RINOK(getProp->GetProp(kpidSize, &prop));
sizeDefined = ConvertPropVariantToUInt64(prop, size);
}
if (sizeDefined)
{
_curSize = size;
_curSizeDefined = true;
}
if (askExtractMode != NArchive::NExtract::NAskMode::kExtract &&
askExtractMode != NArchive::NExtract::NAskMode::kTest)
return S_OK;
_needUpdateStat = true;
CMyComPtr<ISequentialOutStream> outStreamLoc;
if (VirtFileSystem && askExtractMode == NArchive::NExtract::NAskMode::kExtract)
{
CVirtFile &file = VirtFileSystemSpec->AddNewFile();
_newVirtFileWasAdded = true;
file.Name = name;
file.IsDir = IntToBool(isDir);
file.IsAltStream = _isAltStream;
file.Size = 0;
RINOK(GetTime(getProp, kpidCTime, file.CTime, file.CTimeDefined));
RINOK(GetTime(getProp, kpidATime, file.ATime, file.ATimeDefined));
RINOK(GetTime(getProp, kpidMTime, file.MTime, file.MTimeDefined));
NCOM::CPropVariant prop;
RINOK(getProp->GetProp(kpidAttrib, &prop));
if (prop.vt == VT_UI4)
{
file.Attrib = prop.ulVal;
file.AttribDefined = true;
}
// else if (isDir) file.Attrib = FILE_ATTRIBUTE_DIRECTORY;
file.ExpectedSize = 0;
if (sizeDefined)
file.ExpectedSize = size;
outStreamLoc = VirtFileSystem;
}
if (_hashStream)
{
{
_hashStreamSpec->SetStream(outStreamLoc);
outStreamLoc = _hashStream;
_hashStreamSpec->Init(true);
_hashStreamWasUsed = true;
}
}
if (outStreamLoc)
*outStream = outStreamLoc.Detach();
return S_OK;
COM_TRY_END
}
STDMETHODIMP CExtractCallbackImp::PrepareOperation7(Int32 askExtractMode)
{
COM_TRY_BEGIN
_needUpdateStat = (
askExtractMode == NArchive::NExtract::NAskMode::kExtract ||
askExtractMode == NArchive::NExtract::NAskMode::kTest);
/*
_extractMode = false;
switch (askExtractMode)
{
case NArchive::NExtract::NAskMode::kExtract:
if (_testMode)
askExtractMode = NArchive::NExtract::NAskMode::kTest;
else
_extractMode = true;
break;
};
*/
return SetCurrentFilePath2(_filePath);
COM_TRY_END
}
STDMETHODIMP CExtractCallbackImp::SetOperationResult7(Int32 opRes, bool encrypted)
{
COM_TRY_BEGIN
if (VirtFileSystem && _newVirtFileWasAdded)
{
// FIXME: probably we must request file size from VirtFileSystem
// _curSize = VirtFileSystem->GetLastFileSize()
// _curSizeDefined = true;
RINOK(VirtFileSystemSpec->CloseMemFile());
}
if (_hashStream && _hashStreamWasUsed)
{
_hashStreamSpec->_hash->Final(_isFolder, _isAltStream, _filePath);
_curSize = _hashStreamSpec->GetSize();
_curSizeDefined = true;
_hashStreamSpec->ReleaseStream();
_hashStreamWasUsed = false;
}
else if (_hashCalc && _needUpdateStat)
{
_hashCalc->SetSize(_curSize);
_hashCalc->Final(_isFolder, _isAltStream, _filePath);
}
return SetOperationResult(opRes, encrypted);
COM_TRY_END
}
static const size_t k_SizeT_MAX = (size_t)((size_t)0 - 1);
static const UInt32 kBlockSize = ((UInt32)1 << 31);
STDMETHODIMP CVirtFileSystem::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
if (processedSize)
*processedSize = 0;
if (size == 0)
return S_OK;
if (!_fileMode)
{
CVirtFile &file = Files.Back();
size_t rem = file.Data.Size() - (size_t)file.Size;
bool useMem = true;
if (rem < size)
{
UInt64 b = 0;
if (file.Data.Size() == 0)
b = file.ExpectedSize;
UInt64 a = file.Size + size;
if (b < a)
b = a;
a = (UInt64)file.Data.Size() * 2;
if (b < a)
b = a;
useMem = false;
if (b <= k_SizeT_MAX && b <= MaxTotalAllocSize)
useMem = file.Data.ReAlloc_KeepData((size_t)b, (size_t)file.Size);
}
if (useMem)
{
memcpy(file.Data + file.Size, data, size);
file.Size += size;
if (processedSize)
*processedSize = (UInt32)size;
return S_OK;
}
_fileMode = true;
}
RINOK(FlushToDisk(false));
return _outFileStream->Write(data, size, processedSize);
}
HRESULT CVirtFileSystem::FlushToDisk(bool closeLast)
{
if (!_outFileStream)
{
_outFileStreamSpec = new COutFileStream;
_outFileStream = _outFileStreamSpec;
}
while (_numFlushed < Files.Size())
{
const CVirtFile &file = Files[_numFlushed];
const FString path = DirPrefix + us2fs(GetCorrectFsPath(file.Name));
if (!_fileIsOpen)
{
if (!_outFileStreamSpec->Create(path, false))
{
_outFileStream.Release();
return E_FAIL;
// MessageBoxMyError(UString(L"Can't create file ") + fs2us(tempFilePath));
}
_fileIsOpen = true;
RINOK(WriteStream(_outFileStream, file.Data, (size_t)file.Size));
}
if (_numFlushed == Files.Size() - 1 && !closeLast)
break;
if (file.CTimeDefined ||
file.ATimeDefined ||
file.MTimeDefined)
_outFileStreamSpec->SetTime(
file.CTimeDefined ? &file.CTime : NULL,
file.ATimeDefined ? &file.ATime : NULL,
file.MTimeDefined ? &file.MTime : NULL);
_outFileStreamSpec->Close();
_numFlushed++;
_fileIsOpen = false;
if (file.AttribDefined)
NDir::SetFileAttrib(path, file.Attrib);
}
return S_OK;
}
#endif