d074cc7c07
Plugin: ns7zip v2.0.0 Architectures: x86-ansi, x86-unicode, amd64-unicode License: LGPL-2.1-or-later
1273 lines
32 KiB
C++
1273 lines
32 KiB
C++
// 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 Z7_SFX
|
|
#include "../Common/ZipRegistry.h"
|
|
#endif
|
|
|
|
#include "../GUI/ExtractRes.h"
|
|
#include "resourceGui.h"
|
|
|
|
#include "ExtractCallback.h"
|
|
#include "FormatUtils.h"
|
|
#include "LangUtils.h"
|
|
#include "MemDialog.h"
|
|
#include "OverwriteDialog.h"
|
|
#ifndef Z7_NO_CRYPTO
|
|
#include "PasswordDialog.h"
|
|
#endif
|
|
#include "PropertyName.h"
|
|
|
|
using namespace NWindows;
|
|
using namespace NFile;
|
|
using namespace NFind;
|
|
|
|
extern bool g_DisableUserQuestions;
|
|
|
|
CExtractCallbackImp::~CExtractCallbackImp() {}
|
|
|
|
void CExtractCallbackImp::Init()
|
|
{
|
|
LangString(IDS_PROGRESS_EXTRACTING, _lang_Extracting);
|
|
LangString(IDS_PROGRESS_TESTING, _lang_Testing);
|
|
LangString(IDS_PROGRESS_SKIPPING, _lang_Skipping);
|
|
_lang_Reading = "Reading";
|
|
|
|
NumArchiveErrors = 0;
|
|
ThereAreMessageErrors = false;
|
|
#ifndef Z7_SFX
|
|
NumFolders = NumFiles = 0;
|
|
NeedAddFile = false;
|
|
#endif
|
|
}
|
|
|
|
void CExtractCallbackImp::AddError_Message(LPCWSTR s)
|
|
{
|
|
ThereAreMessageErrors = true;
|
|
ProgressDialog->Sync.AddError_Message(s);
|
|
}
|
|
|
|
void CExtractCallbackImp::AddError_Message_ShowArcPath(LPCWSTR s)
|
|
{
|
|
Add_ArchiveName_Error();
|
|
AddError_Message(s);
|
|
}
|
|
|
|
|
|
#ifndef Z7_SFX
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::SetNumFiles(UInt64 numFiles))
|
|
{
|
|
#ifdef Z7_SFX
|
|
UNUSED_VAR(numFiles)
|
|
#else
|
|
ProgressDialog->Sync.Set_NumFilesTotal(numFiles);
|
|
#endif
|
|
return S_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::SetTotal(UInt64 total))
|
|
{
|
|
ProgressDialog->Sync.Set_NumBytesTotal(total);
|
|
return S_OK;
|
|
}
|
|
|
|
Z7_COM7F_IMF(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 *files, const UInt64 *bytes)
|
|
{
|
|
HRESULT res = S_OK;
|
|
if (!MultiArcMode)
|
|
{
|
|
if (files)
|
|
{
|
|
_totalFiles_Defined = true;
|
|
// res = ProgressDialog->Sync.Set_NumFilesTotal(*files);
|
|
}
|
|
else
|
|
_totalFiles_Defined = false;
|
|
|
|
if (bytes)
|
|
{
|
|
_totalBytes_Defined = true;
|
|
ProgressDialog->Sync.Set_NumBytesTotal(*bytes);
|
|
}
|
|
else
|
|
_totalBytes_Defined = false;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
HRESULT CExtractCallbackImp::Open_SetCompleted(const UInt64 *files, const UInt64 *bytes)
|
|
{
|
|
if (!MultiArcMode)
|
|
{
|
|
if (files)
|
|
{
|
|
ProgressDialog->Sync.Set_NumFilesCur(*files);
|
|
}
|
|
|
|
if (bytes)
|
|
{
|
|
}
|
|
}
|
|
|
|
return ProgressDialog->Sync.CheckStop();
|
|
}
|
|
|
|
HRESULT CExtractCallbackImp::Open_Finished()
|
|
{
|
|
return ProgressDialog->Sync.CheckStop();
|
|
}
|
|
|
|
#ifndef Z7_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_Clear_PasswordWasAsked_Flag()
|
|
{
|
|
PasswordWasAsked = false;
|
|
}
|
|
*/
|
|
|
|
#endif
|
|
|
|
|
|
#ifndef Z7_SFX
|
|
Z7_COM7F_IMF(CExtractCallbackImp::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize))
|
|
{
|
|
ProgressDialog->Sync.Set_Ratio(inSize, outSize);
|
|
return S_OK;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Z7_COM7F_IMF(CExtractCallbackImp::SetTotalFiles(UInt64 total)
|
|
{
|
|
ProgressDialog->Sync.SetNumFilesTotal(total);
|
|
return S_OK;
|
|
}
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::SetCompletedFiles(const UInt64 *value)
|
|
{
|
|
if (value != NULL)
|
|
ProgressDialog->Sync.SetNumFilesCur(*value);
|
|
return S_OK;
|
|
}
|
|
*/
|
|
|
|
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))
|
|
{
|
|
COverwriteDialog dialog;
|
|
|
|
dialog.OldFileInfo.SetTime2(existTime);
|
|
dialog.OldFileInfo.SetSize2(existSize);
|
|
dialog.OldFileInfo.Path = existName;
|
|
dialog.OldFileInfo.Is_FileSystemFile = true;
|
|
|
|
dialog.NewFileInfo.SetTime2(newTime);
|
|
dialog.NewFileInfo.SetSize2(newSize);
|
|
dialog.NewFileInfo.Path = newName;
|
|
dialog.NewFileInfo.Is_FileSystemFile = Src_Is_IO_FS_Folder;
|
|
|
|
ProgressDialog->WaitCreating();
|
|
const 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;
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::PrepareOperation(const wchar_t *name, Int32 isFolder, Int32 askExtractMode, const UInt64 * /* position */))
|
|
{
|
|
_isFolder = IntToBool(isFolder);
|
|
_currentFilePath = name;
|
|
|
|
const UString *msg = &_lang_Empty;
|
|
switch (askExtractMode)
|
|
{
|
|
case NArchive::NExtract::NAskMode::kExtract: msg = &_lang_Extracting; break;
|
|
case NArchive::NExtract::NAskMode::kTest: msg = &_lang_Testing; break;
|
|
case NArchive::NExtract::NAskMode::kSkip: msg = &_lang_Skipping; break;
|
|
case NArchive::NExtract::NAskMode::kReadExternal: msg = &_lang_Reading; break;
|
|
// default: s = "Unknown operation";
|
|
}
|
|
|
|
return ProgressDialog->Sync.Set_Status2(*msg, name, IntToBool(isFolder));
|
|
}
|
|
|
|
Z7_COM7F_IMF(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 Z7_SFX
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::ShowMessage(const wchar_t *s))
|
|
{
|
|
AddError_Message(s);
|
|
return S_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
void SetExtractErrorMessage(Int32 opRes, Int32 encrypted, const wchar_t *fileName, UString &s);
|
|
void SetExtractErrorMessage(Int32 opRes, Int32 encrypted, const wchar_t *fileName, UString &s)
|
|
{
|
|
s.Empty();
|
|
|
|
if (opRes == NArchive::NExtract::NOperationResult::kOK)
|
|
return;
|
|
|
|
#ifndef Z7_SFX
|
|
UINT messageID = 0;
|
|
#endif
|
|
UINT id = 0;
|
|
|
|
switch (opRes)
|
|
{
|
|
case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
|
|
#ifndef Z7_SFX
|
|
messageID = IDS_EXTRACT_MESSAGE_UNSUPPORTED_METHOD;
|
|
#endif
|
|
id = IDS_EXTRACT_MSG_UNSUPPORTED_METHOD;
|
|
break;
|
|
case NArchive::NExtract::NOperationResult::kDataError:
|
|
#ifndef Z7_SFX
|
|
messageID = encrypted ?
|
|
IDS_EXTRACT_MESSAGE_DATA_ERROR_ENCRYPTED:
|
|
IDS_EXTRACT_MESSAGE_DATA_ERROR;
|
|
#endif
|
|
id = IDS_EXTRACT_MSG_DATA_ERROR;
|
|
break;
|
|
case NArchive::NExtract::NOperationResult::kCRCError:
|
|
#ifndef Z7_SFX
|
|
messageID = encrypted ?
|
|
IDS_EXTRACT_MESSAGE_CRC_ERROR_ENCRYPTED:
|
|
IDS_EXTRACT_MESSAGE_CRC_ERROR;
|
|
#endif
|
|
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;
|
|
case NArchive::NExtract::NOperationResult::kWrongPassword:
|
|
id = IDS_EXTRACT_MSG_WRONG_PSW_CLAIM;
|
|
break;
|
|
/*
|
|
default:
|
|
messageID = IDS_EXTRACT_MESSAGE_UNKNOWN_ERROR;
|
|
break;
|
|
*/
|
|
}
|
|
|
|
UString msg;
|
|
|
|
#ifndef Z7_SFX
|
|
UString msgOld;
|
|
#ifdef Z7_LANG
|
|
if (id != 0)
|
|
LangString_OnlyFromLangFile(id, msg);
|
|
if (messageID != 0 && msg.IsEmpty())
|
|
LangString_OnlyFromLangFile(messageID, msgOld);
|
|
#endif
|
|
if (msg.IsEmpty() && !msgOld.IsEmpty())
|
|
s = MyFormatNew(msgOld, fileName);
|
|
else
|
|
#endif
|
|
{
|
|
if (msg.IsEmpty() && id != 0)
|
|
LangString(id, msg);
|
|
if (!msg.IsEmpty())
|
|
s += msg;
|
|
else
|
|
{
|
|
s += "Error #";
|
|
s.Add_UInt32((UInt32)opRes);
|
|
}
|
|
|
|
if (encrypted && opRes != NArchive::NExtract::NOperationResult::kWrongPassword)
|
|
{
|
|
// s += " : ";
|
|
// AddLangString(s, IDS_EXTRACT_MSG_ENCRYPTED);
|
|
s += " : ";
|
|
AddLangString(s, IDS_EXTRACT_MSG_WRONG_PSW_GUESS);
|
|
}
|
|
s += " : ";
|
|
s += fileName;
|
|
}
|
|
}
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::SetOperationResult(Int32 opRes, Int32 encrypted))
|
|
{
|
|
switch (opRes)
|
|
{
|
|
case NArchive::NExtract::NOperationResult::kOK:
|
|
break;
|
|
default:
|
|
{
|
|
UString s;
|
|
SetExtractErrorMessage(opRes, encrypted, _currentFilePath, s);
|
|
AddError_Message_ShowArcPath(s);
|
|
}
|
|
}
|
|
|
|
_currentFilePath.Empty();
|
|
#ifndef Z7_SFX
|
|
if (_isFolder)
|
|
NumFolders++;
|
|
else
|
|
NumFiles++;
|
|
ProgressDialog->Sync.Set_NumFilesCur(NumFiles);
|
|
#endif
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::ReportExtractResult(Int32 opRes, Int32 encrypted, const wchar_t *name))
|
|
{
|
|
if (opRes != NArchive::NExtract::NOperationResult::kOK)
|
|
{
|
|
UString s;
|
|
SetExtractErrorMessage(opRes, encrypted, name, s);
|
|
AddError_Message_ShowArcPath(s);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// IExtractCallbackUI
|
|
|
|
HRESULT CExtractCallbackImp::BeforeOpen(const wchar_t *name, bool /* testMode */)
|
|
{
|
|
_currentArchivePath = name;
|
|
_needWriteArchivePath = true;
|
|
#ifndef Z7_SFX
|
|
RINOK(ProgressDialog->Sync.CheckStop())
|
|
ProgressDialog->Sync.Set_TitleFileName(name);
|
|
#endif
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CExtractCallbackImp::SetCurrentFilePath2(const wchar_t *path)
|
|
{
|
|
_currentFilePath = path;
|
|
#ifndef Z7_SFX
|
|
ProgressDialog->Sync.Set_FilePath(path);
|
|
#endif
|
|
return S_OK;
|
|
}
|
|
|
|
#ifndef Z7_SFX
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::SetCurrentFilePath(const wchar_t *path))
|
|
{
|
|
#ifndef Z7_SFX
|
|
if (NeedAddFile)
|
|
NumFiles++;
|
|
NeedAddFile = true;
|
|
ProgressDialog->Sync.Set_NumFilesCur(NumFiles);
|
|
#endif
|
|
return SetCurrentFilePath2(path);
|
|
}
|
|
|
|
#endif
|
|
|
|
UString HResultToMessage(HRESULT errorCode);
|
|
|
|
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
|
|
};
|
|
|
|
static void AddNewLineString(UString &s, const UString &m)
|
|
{
|
|
s += m;
|
|
s.Add_LF();
|
|
}
|
|
|
|
UString GetOpenArcErrorMessage(UInt32 errorFlags);
|
|
UString GetOpenArcErrorMessage(UInt32 errorFlags)
|
|
{
|
|
UString s;
|
|
|
|
for (unsigned i = 0; i < Z7_ARRAY_SIZE(k_ErrorFlagsIds); i++)
|
|
{
|
|
const UInt32 f = (UInt32)1 << i;
|
|
if ((errorFlags & f) == 0)
|
|
continue;
|
|
const UInt32 id = k_ErrorFlagsIds[i];
|
|
UString m = LangString(id);
|
|
if (m.IsEmpty())
|
|
continue;
|
|
if (f == kpv_ErrorFlags_EncryptedHeadersError)
|
|
{
|
|
m += " : ";
|
|
AddLangString(m, IDS_EXTRACT_MSG_WRONG_PSW_GUESS);
|
|
}
|
|
if (!s.IsEmpty())
|
|
s.Add_LF();
|
|
s += m;
|
|
errorFlags &= ~f;
|
|
}
|
|
|
|
if (errorFlags != 0)
|
|
{
|
|
char sz[16];
|
|
sz[0] = '0';
|
|
sz[1] = 'x';
|
|
ConvertUInt32ToHex(errorFlags, sz + 2);
|
|
if (!s.IsEmpty())
|
|
s.Add_LF();
|
|
s += sz;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
static void ErrorInfo_Print(UString &s, const CArcErrorInfo &er)
|
|
{
|
|
const UInt32 errorFlags = er.GetErrorFlags();
|
|
const UInt32 warningFlags = er.GetWarningFlags();
|
|
|
|
if (errorFlags != 0)
|
|
AddNewLineString(s, GetOpenArcErrorMessage(errorFlags));
|
|
|
|
if (!er.ErrorMessage.IsEmpty())
|
|
AddNewLineString(s, er.ErrorMessage);
|
|
|
|
if (warningFlags != 0)
|
|
{
|
|
s += GetNameOfProperty(kpidWarningFlags, L"Warnings");
|
|
s.Add_Colon();
|
|
s.Add_LF();
|
|
AddNewLineString(s, GetOpenArcErrorMessage(warningFlags));
|
|
}
|
|
|
|
if (!er.WarningMessage.IsEmpty())
|
|
{
|
|
s += GetNameOfProperty(kpidWarning, L"Warning");
|
|
s += ": ";
|
|
s += er.WarningMessage;
|
|
s.Add_LF();
|
|
}
|
|
}
|
|
|
|
static UString GetBracedType(const wchar_t *type)
|
|
{
|
|
UString s ('[');
|
|
s += type;
|
|
s.Add_Char(']');
|
|
return s;
|
|
}
|
|
|
|
void OpenResult_GUI(UString &s, const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result);
|
|
void OpenResult_GUI(UString &s, const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result)
|
|
{
|
|
FOR_VECTOR (level, arcLink.Arcs)
|
|
{
|
|
const CArc &arc = arcLink.Arcs[level];
|
|
const CArcErrorInfo &er = arc.ErrorInfo;
|
|
|
|
if (!er.IsThereErrorOrWarning() && er.ErrorFormatIndex < 0)
|
|
continue;
|
|
|
|
if (s.IsEmpty())
|
|
{
|
|
s += name;
|
|
s.Add_LF();
|
|
}
|
|
|
|
if (level != 0)
|
|
{
|
|
AddNewLineString(s, arc.Path);
|
|
}
|
|
|
|
ErrorInfo_Print(s, er);
|
|
|
|
if (er.ErrorFormatIndex >= 0)
|
|
{
|
|
AddNewLineString(s, GetNameOfProperty(kpidWarning, L"Warning"));
|
|
if (arc.FormatIndex == er.ErrorFormatIndex)
|
|
{
|
|
AddNewLineString(s, LangString(IDS_IS_OPEN_WITH_OFFSET));
|
|
}
|
|
else
|
|
{
|
|
AddNewLineString(s, MyFormatNew(IDS_CANT_OPEN_AS_TYPE, GetBracedType(codecs->GetFormatNamePtr(er.ErrorFormatIndex))));
|
|
AddNewLineString(s, MyFormatNew(IDS_IS_OPEN_AS_TYPE, GetBracedType(codecs->GetFormatNamePtr(arc.FormatIndex))));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0 || result != S_OK)
|
|
{
|
|
s += name;
|
|
s.Add_LF();
|
|
if (!arcLink.Arcs.IsEmpty())
|
|
AddNewLineString(s, arcLink.NonOpen_ArcPath);
|
|
|
|
if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0 || result == S_FALSE)
|
|
{
|
|
UINT id = IDS_CANT_OPEN_ARCHIVE;
|
|
UString param;
|
|
if (arcLink.PasswordWasAsked)
|
|
id = IDS_CANT_OPEN_ENCRYPTED_ARCHIVE;
|
|
else if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0)
|
|
{
|
|
id = IDS_CANT_OPEN_AS_TYPE;
|
|
param = GetBracedType(codecs->GetFormatNamePtr(arcLink.NonOpen_ErrorInfo.ErrorFormatIndex));
|
|
}
|
|
UString s2 = MyFormatNew(id, param);
|
|
s2.Replace(L" ''", L"");
|
|
s2.Replace(L"''", L"");
|
|
s += s2;
|
|
}
|
|
else
|
|
s += HResultToMessage(result);
|
|
|
|
s.Add_LF();
|
|
ErrorInfo_Print(s, arcLink.NonOpen_ErrorInfo);
|
|
}
|
|
|
|
if (!s.IsEmpty() && s.Back() == '\n')
|
|
s.DeleteBack();
|
|
}
|
|
|
|
HRESULT CExtractCallbackImp::OpenResult(const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result)
|
|
{
|
|
_currentArchivePath = name;
|
|
_needWriteArchivePath = true;
|
|
|
|
UString s;
|
|
OpenResult_GUI(s, codecs, arcLink, name, result);
|
|
if (!s.IsEmpty())
|
|
{
|
|
NumArchiveErrors++;
|
|
AddError_Message(s);
|
|
_needWriteArchivePath = false;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CExtractCallbackImp::ThereAreNoFiles()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
void CExtractCallbackImp::Add_ArchiveName_Error()
|
|
{
|
|
if (_needWriteArchivePath)
|
|
{
|
|
if (!_currentArchivePath.IsEmpty())
|
|
AddError_Message(_currentArchivePath);
|
|
_needWriteArchivePath = false;
|
|
}
|
|
}
|
|
|
|
HRESULT CExtractCallbackImp::ExtractResult(HRESULT result)
|
|
{
|
|
#ifndef Z7_SFX
|
|
ProgressDialog->Sync.Set_FilePath(L"");
|
|
#endif
|
|
|
|
if (result == S_OK)
|
|
return result;
|
|
NumArchiveErrors++;
|
|
if (result == E_ABORT
|
|
|| result == HRESULT_FROM_WIN32(ERROR_DISK_FULL)
|
|
)
|
|
return result;
|
|
|
|
Add_ArchiveName_Error();
|
|
if (!_currentFilePath.IsEmpty())
|
|
MessageError(_currentFilePath);
|
|
MessageError(NError::MyFormatMessage(result));
|
|
return S_OK;
|
|
}
|
|
|
|
#ifndef Z7_NO_CRYPTO
|
|
|
|
HRESULT CExtractCallbackImp::SetPassword(const UString &password)
|
|
{
|
|
PasswordIsDefined = true;
|
|
Password = password;
|
|
return S_OK;
|
|
}
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::CryptoGetTextPassword(BSTR *password))
|
|
{
|
|
PasswordWasAsked = true;
|
|
if (!PasswordIsDefined)
|
|
{
|
|
CPasswordDialog dialog;
|
|
#ifndef Z7_SFX
|
|
const 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 Z7_SFX
|
|
if (dialog.ShowPassword != showPassword)
|
|
NExtract::Save_ShowPassword(dialog.ShowPassword);
|
|
#endif
|
|
}
|
|
return StringToBstr(Password, password);
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef Z7_SFX
|
|
|
|
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))
|
|
{
|
|
UString destPathResultTemp = destPath;
|
|
|
|
// RINOK(StringToBstr(destPath, destPathResult));
|
|
|
|
*destPathResult = NULL;
|
|
*writeAnswer = BoolToInt(false);
|
|
|
|
FString destPathSys = us2fs(destPath);
|
|
const bool srcIsFolderSpec = IntToBool(srcIsFolder);
|
|
CFileInfo destFileInfo;
|
|
|
|
if (destFileInfo.Find(destPathSys))
|
|
{
|
|
if (srcIsFolderSpec)
|
|
{
|
|
if (!destFileInfo.IsDir())
|
|
{
|
|
RINOK(MessageError("Cannot replace file with folder with same name", destPathSys))
|
|
return E_ABORT;
|
|
}
|
|
*writeAnswer = BoolToInt(false);
|
|
return S_OK;
|
|
}
|
|
|
|
if (destFileInfo.IsDir())
|
|
{
|
|
RINOK(MessageError("Cannot replace folder with file with same name", destPathSys))
|
|
*writeAnswer = BoolToInt(false);
|
|
return S_OK;
|
|
}
|
|
|
|
switch ((int)OverwriteMode)
|
|
{
|
|
case NExtract::NOverwriteMode::kSkip:
|
|
return S_OK;
|
|
case NExtract::NOverwriteMode::kAsk:
|
|
{
|
|
Int32 overwriteResult;
|
|
UString destPathSpec = destPath;
|
|
const int slashPos = destPathSpec.ReverseFind_PathSepar();
|
|
destPathSpec.DeleteFrom((unsigned)(slashPos + 1));
|
|
destPathSpec += fs2us(destFileInfo.Name);
|
|
|
|
RINOK(AskOverwrite(
|
|
destPathSpec,
|
|
&destFileInfo.MTime, &destFileInfo.Size,
|
|
srcPath,
|
|
srcTime, srcSize,
|
|
&overwriteResult))
|
|
|
|
switch (overwriteResult)
|
|
{
|
|
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;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (OverwriteMode == NExtract::NOverwriteMode::kRename)
|
|
{
|
|
if (!AutoRenamePath(destPathSys))
|
|
{
|
|
RINOK(MessageError("Cannot create name for file", destPathSys))
|
|
return E_ABORT;
|
|
}
|
|
destPathResultTemp = fs2us(destPathSys);
|
|
}
|
|
else
|
|
{
|
|
if (NFind::DoesFileExist_Raw(destPathSys))
|
|
if (!NDir::DeleteFileAlways(destPathSys))
|
|
if (GetLastError() != ERROR_FILE_NOT_FOUND)
|
|
{
|
|
RINOK(MessageError("Cannot delete output file", destPathSys))
|
|
return E_ABORT;
|
|
}
|
|
}
|
|
}
|
|
*writeAnswer = BoolToInt(true);
|
|
return StringToBstr(destPathResultTemp, destPathResult);
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(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;
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::GetStream7(const wchar_t *name,
|
|
Int32 isDir,
|
|
ISequentialOutStream **outStream, Int32 askExtractMode,
|
|
IGetProp *getProp))
|
|
{
|
|
COM_TRY_BEGIN
|
|
*outStream = NULL;
|
|
_newVirtFileWasAdded = false;
|
|
_hashStream_WasUsed = false;
|
|
_needUpdateStat = false;
|
|
_isFolder = IntToBool(isDir);
|
|
_curSize_Defined = false;
|
|
_curSize = 0;
|
|
|
|
if (_hashStream)
|
|
_hashStream->ReleaseStream();
|
|
|
|
_filePath = name;
|
|
|
|
UInt64 size = 0;
|
|
bool size_Defined;
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(getProp->GetProp(kpidSize, &prop))
|
|
size_Defined = ConvertPropVariantToUInt64(prop, size);
|
|
}
|
|
if (size_Defined)
|
|
{
|
|
_curSize = size;
|
|
_curSize_Defined = true;
|
|
}
|
|
|
|
GetItemBoolProp(getProp, kpidIsAltStream, _isAltStream);
|
|
if (!ProcessAltStreams && _isAltStream)
|
|
return S_OK;
|
|
|
|
if (isDir) // we don't support dir items extraction in this code
|
|
return S_OK;
|
|
|
|
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)
|
|
{
|
|
if (!VirtFileSystemSpec->Files.IsEmpty())
|
|
VirtFileSystemSpec->MaxTotalAllocSize -= VirtFileSystemSpec->Files.Back().Data.Size();
|
|
CVirtFile &file = VirtFileSystemSpec->Files.AddNew();
|
|
_newVirtFileWasAdded = true;
|
|
// file.IsDir = _isFolder;
|
|
file.IsAltStream = _isAltStream;
|
|
file.WrittenSize = 0;
|
|
file.ExpectedSize = 0;
|
|
if (size_Defined)
|
|
file.ExpectedSize = size;
|
|
|
|
if (VirtFileSystemSpec->Index_of_MainExtractedFile_in_Files < 0)
|
|
if (!file.IsAltStream || VirtFileSystemSpec->IsAltStreamFile)
|
|
VirtFileSystemSpec->Index_of_MainExtractedFile_in_Files =
|
|
(int)(VirtFileSystemSpec->Files.Size() - 1);
|
|
|
|
/* if we open only AltStream, then (name) contains only name without "fileName:" prefix */
|
|
file.BaseName = name;
|
|
|
|
if (file.IsAltStream
|
|
&& !VirtFileSystemSpec->IsAltStreamFile
|
|
&& file.BaseName.IsPrefixedBy_NoCase(VirtFileSystemSpec->FileName))
|
|
{
|
|
const unsigned colonPos = VirtFileSystemSpec->FileName.Len();
|
|
if (file.BaseName[colonPos] == ':')
|
|
{
|
|
file.ColonWasUsed = true;
|
|
file.AltStreamName = name + (size_t)colonPos + 1;
|
|
file.BaseName.DeleteFrom(colonPos);
|
|
if (Is_ZoneId_StreamName(file.AltStreamName))
|
|
{
|
|
if (VirtFileSystemSpec->Index_of_ZoneBuf_AltStream_in_Files < 0)
|
|
VirtFileSystemSpec->Index_of_ZoneBuf_AltStream_in_Files =
|
|
(int)(VirtFileSystemSpec->Files.Size() - 1);
|
|
}
|
|
}
|
|
}
|
|
RINOK(GetTime(getProp, kpidCTime, file.CTime, file.CTime_Defined))
|
|
RINOK(GetTime(getProp, kpidATime, file.ATime, file.ATime_Defined))
|
|
RINOK(GetTime(getProp, kpidMTime, file.MTime, file.MTime_Defined))
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(getProp->GetProp(kpidAttrib, &prop))
|
|
if (prop.vt == VT_UI4)
|
|
{
|
|
file.Attrib = prop.ulVal;
|
|
file.Attrib_Defined = true;
|
|
}
|
|
}
|
|
outStreamLoc = VirtFileSystem;
|
|
}
|
|
|
|
if (_hashStream)
|
|
{
|
|
_hashStream->SetStream(outStreamLoc);
|
|
outStreamLoc = _hashStream;
|
|
_hashStream->Init(true);
|
|
_hashStream_WasUsed = true;
|
|
}
|
|
|
|
if (outStreamLoc)
|
|
*outStream = outStreamLoc.Detach();
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::PrepareOperation7(Int32 askExtractMode))
|
|
{
|
|
COM_TRY_BEGIN
|
|
_needUpdateStat = (
|
|
askExtractMode == NArchive::NExtract::NAskMode::kExtract
|
|
|| askExtractMode == NArchive::NExtract::NAskMode::kTest
|
|
|| askExtractMode == NArchive::NExtract::NAskMode::kReadExternal
|
|
);
|
|
|
|
/*
|
|
_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
|
|
}
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::SetOperationResult8(Int32 opRes, Int32 encrypted, UInt64 size))
|
|
{
|
|
COM_TRY_BEGIN
|
|
if (VirtFileSystem && _newVirtFileWasAdded)
|
|
{
|
|
// FIXME: probably we must request file size from VirtFileSystem
|
|
// _curSize = VirtFileSystem->GetLastFileSize()
|
|
// _curSize_Defined = true;
|
|
RINOK(VirtFileSystemSpec->CloseMemFile())
|
|
}
|
|
if (_hashStream && _hashStream_WasUsed)
|
|
{
|
|
_hashStream->_hash->Final(_isFolder, _isAltStream, _filePath);
|
|
_curSize = _hashStream->GetSize();
|
|
_curSize_Defined = true;
|
|
_hashStream->ReleaseStream();
|
|
_hashStream_WasUsed = false;
|
|
}
|
|
else if (_hashCalc && _needUpdateStat)
|
|
{
|
|
_hashCalc->SetSize(size); // (_curSize) before 21.04
|
|
_hashCalc->Final(_isFolder, _isAltStream, _filePath);
|
|
}
|
|
return SetOperationResult(opRes, encrypted);
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CExtractCallbackImp::RequestMemoryUse(
|
|
UInt32 flags, UInt32 indexType, UInt32 /* index */, const wchar_t *path,
|
|
UInt64 requiredSize, UInt64 *allowedSize, UInt32 *answerFlags))
|
|
{
|
|
UInt32 limit_GB = (UInt32)((*allowedSize + ((1u << 30) - 1)) >> 30);
|
|
|
|
if ((flags & NRequestMemoryUseFlags::k_IsReport) == 0)
|
|
{
|
|
UInt64 limit_bytes = *allowedSize;
|
|
const UInt32 limit_GB_Registry = NExtract::Read_LimitGB();
|
|
if (limit_GB_Registry != 0 && limit_GB_Registry != (UInt32)(Int32)-1)
|
|
{
|
|
const UInt64 limit_bytes_Registry = (UInt64)limit_GB_Registry << 30;
|
|
// registry_WasForced = true;
|
|
if ((flags & NRequestMemoryUseFlags::k_AllowedSize_WasForced) == 0
|
|
|| limit_bytes < limit_bytes_Registry)
|
|
{
|
|
limit_bytes = limit_bytes_Registry;
|
|
limit_GB = limit_GB_Registry;
|
|
}
|
|
}
|
|
*allowedSize = limit_bytes;
|
|
if (requiredSize <= limit_bytes)
|
|
{
|
|
*answerFlags = NRequestMemoryAnswerFlags::k_Allow;
|
|
return S_OK;
|
|
}
|
|
// default answer can be k_Allow, if limit was not forced,
|
|
// so we change answer to non-allowed here,
|
|
// because user has chance to change limit in GUI.
|
|
*answerFlags = NRequestMemoryAnswerFlags::k_Limit_Exceeded;
|
|
if (flags & NRequestMemoryUseFlags::k_SkipArc_IsExpected)
|
|
*answerFlags |= NRequestMemoryAnswerFlags::k_SkipArc;
|
|
}
|
|
|
|
const UInt32 required_GB = (UInt32)((requiredSize + ((1u << 30) - 1)) >> 30);
|
|
|
|
CMemDialog dialog;
|
|
dialog.Limit_GB = limit_GB;
|
|
dialog.Required_GB = required_GB;
|
|
dialog.TestMode = TestMode;
|
|
if (MultiArcMode)
|
|
dialog.ArcPath = _currentArchivePath;
|
|
if (path)
|
|
dialog.FilePath = path;
|
|
|
|
if (!g_DisableUserQuestions
|
|
&& (flags & NRequestMemoryUseFlags::k_IsReport) == 0)
|
|
{
|
|
if (_remember)
|
|
dialog.SkipArc = _skipArc;
|
|
else
|
|
{
|
|
dialog.ShowRemember =
|
|
(MultiArcMode
|
|
|| indexType != NArchive::NEventIndexType::kNoIndex
|
|
|| path);
|
|
ProgressDialog->WaitCreating();
|
|
if (dialog.Create(*ProgressDialog) != IDCONTINUE)
|
|
{
|
|
*answerFlags = NRequestMemoryAnswerFlags::k_Stop;
|
|
return E_ABORT;
|
|
}
|
|
if (dialog.NeedSave)
|
|
NExtract::Save_LimitGB(dialog.Limit_GB);
|
|
if (dialog.Remember)
|
|
{
|
|
_remember = true;
|
|
_skipArc = dialog.SkipArc;
|
|
}
|
|
}
|
|
|
|
*allowedSize = (UInt64)dialog.Limit_GB << 30;
|
|
if (!dialog.SkipArc)
|
|
{
|
|
*answerFlags = NRequestMemoryAnswerFlags::k_Allow;
|
|
return S_OK;
|
|
}
|
|
*answerFlags =
|
|
NRequestMemoryAnswerFlags::k_SkipArc
|
|
| NRequestMemoryAnswerFlags::k_Limit_Exceeded;
|
|
flags |= NRequestMemoryUseFlags::k_Report_SkipArc;
|
|
}
|
|
|
|
if ((flags & NRequestMemoryUseFlags::k_NoErrorMessage) == 0)
|
|
{
|
|
UString s ("ERROR: ");
|
|
dialog.AddInfoMessage_To_String(s);
|
|
s.Add_LF();
|
|
// if (indexType == NArchive::NEventIndexType::kNoIndex)
|
|
if ((flags & NRequestMemoryUseFlags::k_SkipArc_IsExpected) ||
|
|
(flags & NRequestMemoryUseFlags::k_Report_SkipArc))
|
|
AddLangString(s, IDS_MSG_ARC_UNPACKING_WAS_SKIPPED);
|
|
/*
|
|
else
|
|
AddLangString(, IDS_MSG_ARC_FILES_UNPACKING_WAS_SKIPPED);
|
|
*/
|
|
AddError_Message_ShowArcPath(s);
|
|
}
|
|
|
|
/*
|
|
if ((flags & NRequestMemoryUseFlags::k_IsReport) == 0)
|
|
*answerFlags |= NRequestMemoryAnswerFlags::k_Limit_Exceeded;
|
|
*/
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CVirtFileSystem::Write(const void *data, UInt32 size, UInt32 *processedSize))
|
|
{
|
|
if (processedSize)
|
|
*processedSize = 0;
|
|
if (size == 0)
|
|
return S_OK;
|
|
if (!_wasSwitchedToFsMode)
|
|
{
|
|
CVirtFile &file = Files.Back();
|
|
const size_t rem = file.Data.Size() - file.WrittenSize;
|
|
bool useMem = true;
|
|
if (rem < size)
|
|
{
|
|
UInt64 b = 0;
|
|
if (file.Data.Size() == 0)
|
|
b = file.ExpectedSize;
|
|
UInt64 a = (UInt64)file.WrittenSize + size;
|
|
if (b < a)
|
|
b = a;
|
|
a = (UInt64)file.Data.Size() * 2;
|
|
if (b < a)
|
|
b = a;
|
|
useMem = false;
|
|
if (b <= MaxTotalAllocSize)
|
|
useMem = file.Data.ReAlloc_KeepData((size_t)b, file.WrittenSize);
|
|
}
|
|
|
|
#if 0 // 1 for debug : FLUSHING TO FS
|
|
useMem = false;
|
|
#endif
|
|
|
|
if (useMem)
|
|
{
|
|
memcpy(file.Data + file.WrittenSize, data, size);
|
|
file.WrittenSize += size;
|
|
if (processedSize)
|
|
*processedSize = (UInt32)size;
|
|
return S_OK;
|
|
}
|
|
_wasSwitchedToFsMode = true;
|
|
}
|
|
|
|
if (!_newVirtFileStream_IsReadyToWrite) // we check for _newVirtFileStream_IsReadyToWrite to optimize execution
|
|
{
|
|
RINOK(FlushToDisk(false))
|
|
}
|
|
|
|
if (_needWriteToRealFile)
|
|
return _outFileStream.Interface()->Write(data, size, processedSize);
|
|
if (processedSize)
|
|
*processedSize = size;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CVirtFileSystem::FlushToDisk(bool closeLast)
|
|
{
|
|
while (_numFlushed < Files.Size())
|
|
{
|
|
CVirtFile &file = Files[_numFlushed];
|
|
const FString basePath = DirPrefix + us2fs(Get_Correct_FsFile_Name(file.BaseName));
|
|
FString path = basePath;
|
|
|
|
if (file.ColonWasUsed)
|
|
{
|
|
if (ZoneBuf.Size() != 0
|
|
&& Is_ZoneId_StreamName(file.AltStreamName))
|
|
{
|
|
// it's expected that
|
|
// CArchiveExtractCallback::GetStream() have excluded
|
|
// ZoneId alt stream from extraction already.
|
|
// But we exclude alt stream extraction here too.
|
|
_numFlushed++;
|
|
continue;
|
|
}
|
|
path.Add_Colon();
|
|
path += us2fs(Get_Correct_FsFile_Name(file.AltStreamName));
|
|
}
|
|
|
|
if (!_newVirtFileStream_IsReadyToWrite)
|
|
{
|
|
if (file.ColonWasUsed)
|
|
{
|
|
NFind::CFileInfo parentFi;
|
|
if (parentFi.Find(basePath)
|
|
&& parentFi.IsReadOnly())
|
|
{
|
|
_altStream_NeedRestore_Attrib_bool = true;
|
|
_altStream_NeedRestore_AttribVal = parentFi.Attrib;
|
|
NDir::SetFileAttrib(basePath, parentFi.Attrib & ~(DWORD)FILE_ATTRIBUTE_READONLY);
|
|
}
|
|
}
|
|
_outFileStream.Create_if_Empty();
|
|
_needWriteToRealFile = _outFileStream->Create_NEW(path);
|
|
if (!_needWriteToRealFile)
|
|
{
|
|
if (!file.ColonWasUsed)
|
|
return GetLastError_noZero_HRESULT(); // it's main file and we can't ignore such error.
|
|
// (file.ColonWasUsed == true)
|
|
// So it's additional alt stream.
|
|
// And we ignore file creation error for additional alt stream.
|
|
// ShowErrorMessage(UString("Can't create file ") + fs2us(path));
|
|
}
|
|
_newVirtFileStream_IsReadyToWrite = true;
|
|
// _openFilePath = path;
|
|
HRESULT hres = S_OK;
|
|
if (_needWriteToRealFile)
|
|
hres = WriteStream(_outFileStream, file.Data, file.WrittenSize);
|
|
// we free allocated memory buffer after data flushing:
|
|
file.WrittenSize = 0;
|
|
file.Data.Free();
|
|
RINOK(hres)
|
|
}
|
|
|
|
if (_numFlushed == Files.Size() - 1 && !closeLast)
|
|
break;
|
|
|
|
if (_needWriteToRealFile)
|
|
{
|
|
if (file.CTime_Defined ||
|
|
file.ATime_Defined ||
|
|
file.MTime_Defined)
|
|
_outFileStream->SetTime(
|
|
file.CTime_Defined ? &file.CTime : NULL,
|
|
file.ATime_Defined ? &file.ATime : NULL,
|
|
file.MTime_Defined ? &file.MTime : NULL);
|
|
_outFileStream->Close();
|
|
}
|
|
|
|
_numFlushed++;
|
|
_newVirtFileStream_IsReadyToWrite = false;
|
|
|
|
if (_needWriteToRealFile)
|
|
{
|
|
if (!file.ColonWasUsed
|
|
&& ZoneBuf.Size() != 0)
|
|
WriteZoneFile_To_BaseFile(path, ZoneBuf);
|
|
if (file.Attrib_Defined)
|
|
NDir::SetFileAttrib_PosixHighDetect(path, file.Attrib);
|
|
// _openFilePath.Empty();
|
|
_needWriteToRealFile = false;
|
|
}
|
|
|
|
if (_altStream_NeedRestore_Attrib_bool)
|
|
{
|
|
_altStream_NeedRestore_Attrib_bool = false;
|
|
NDir::SetFileAttrib(basePath, _altStream_NeedRestore_AttribVal);
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
#endif
|