p7zip/CPP/7zip/UI/FileManager/PanelCrc.cpp
2017-10-11 12:35:36 +02:00

387 lines
8.5 KiB
C++

// PanelCrc.cpp
#include "StdAfx.h"
#include "../../../Common/MyException.h"
#include "../../../Windows/FileFind.h"
#include "../../../Windows/FileIO.h"
#include "../../../Windows/FileName.h"
#include "../Common/LoadCodecs.h"
#include "../GUI/HashGUI.h"
#include "App.h"
#include "LangUtils.h"
#include "resource.h"
using namespace NWindows;
using namespace NFile;
#ifdef EXTERNAL_CODECS
extern CExternalCodecs g_ExternalCodecs;
HRESULT LoadGlobalCodecs();
#endif
static const UInt32 kBufSize = (1 << 15);
struct CDirEnumerator
{
bool EnterToDirs;
FString BasePrefix;
FString BasePrefix_for_Open;
FStringVector FilePaths;
CObjectVector<NFind::CEnumerator> Enumerators;
FStringVector Prefixes;
unsigned Index;
CDirEnumerator(): EnterToDirs(false), Index(0) {};
void Init();
DWORD GetNextFile(NFind::CFileInfo &fi, bool &filled, FString &resPath);
};
void CDirEnumerator::Init()
{
Enumerators.Clear();
Prefixes.Clear();
Index = 0;
}
static DWORD GetNormalizedError()
{
DWORD error = GetLastError();
return (error == 0) ? E_FAIL : error;
}
DWORD CDirEnumerator::GetNextFile(NFind::CFileInfo &fi, bool &filled, FString &resPath)
{
filled = false;
resPath.Empty();
for (;;)
{
#if defined(_WIN32) && !defined(UNDER_CE)
bool isRootPrefix = (BasePrefix.IsEmpty() || (NName::IsSuperPath(BasePrefix) && BasePrefix[NName::kSuperPathPrefixSize] == 0));
#endif
if (Enumerators.IsEmpty())
{
if (Index >= FilePaths.Size())
return S_OK;
const FString &path = FilePaths[Index++];
int pos = path.ReverseFind_PathSepar();
if (pos >= 0)
resPath.SetFrom(path, pos + 1);
#if defined(_WIN32) && !defined(UNDER_CE)
if (isRootPrefix && path.Len() == 2 && NName::IsDrivePath2(path))
{
// we use "c:" item as directory item
fi.ClearBase();
fi.Name = path;
fi.SetAsDir();
fi.Size = 0;
}
else
#endif
if (!fi.Find(BasePrefix + path))
{
DWORD error = GetNormalizedError();
resPath = path;
return error;
}
break;
}
bool found;
if (Enumerators.Back().Next(fi, found))
{
if (found)
{
resPath = Prefixes.Back();
break;
}
}
else
{
DWORD error = GetNormalizedError();
resPath = Prefixes.Back();
Enumerators.DeleteBack();
Prefixes.DeleteBack();
return error;
}
Enumerators.DeleteBack();
Prefixes.DeleteBack();
}
resPath += fi.Name;
if (EnterToDirs && fi.IsDir())
{
FString s = resPath;
s.Add_PathSepar();
Prefixes.Add(s);
s += FCHAR_ANY_MASK;
Enumerators.Add(NFind::CEnumerator(BasePrefix + s));
}
filled = true;
return S_OK;
}
class CThreadCrc: public CProgressThreadVirt
{
HRESULT ProcessVirt();
public:
CDirEnumerator Enumerator;
CHashBundle Hash;
void SetStatus(const UString &s);
void AddErrorMessage(DWORD systemError, const FChar *name);
};
void CThreadCrc::AddErrorMessage(DWORD systemError, const FChar *name)
{
ProgressDialog.Sync.AddError_Code_Name(systemError, fs2us(Enumerator.BasePrefix + name));
Hash.NumErrors++;
}
void CThreadCrc::SetStatus(const UString &s2)
{
UString s = s2;
if (!Enumerator.BasePrefix.IsEmpty())
{
s.Add_Space_if_NotEmpty();
s += fs2us(Enumerator.BasePrefix);
}
ProgressDialog.Sync.Set_Status(s);
}
HRESULT CThreadCrc::ProcessVirt()
{
Hash.Init();
CMyBuffer buf;
if (!buf.Allocate(kBufSize))
return E_OUTOFMEMORY;
CProgressSync &sync = ProgressDialog.Sync;
SetStatus(LangString(IDS_SCANNING));
Enumerator.Init();
FString path;
NFind::CFileInfo fi;
UInt64 numFiles = 0;
UInt64 numItems = 0, numItems_Prev = 0;
UInt64 totalSize = 0;
for (;;)
{
bool filled;
DWORD error = Enumerator.GetNextFile(fi, filled, path);
if (error != 0)
{
AddErrorMessage(error, path);
continue;
}
if (!filled)
break;
if (!fi.IsDir())
{
totalSize += fi.Size;
numFiles++;
}
numItems++;
bool needPrint = false;
// if (fi.IsDir())
{
if (numItems - numItems_Prev >= 100)
{
needPrint = true;
numItems_Prev = numItems;
}
}
/*
else if (numFiles - numFiles_Prev >= 200)
{
needPrint = true;
numFiles_Prev = numFiles;
}
*/
if (needPrint)
{
RINOK(sync.ScanProgress(numFiles, totalSize, path, fi.IsDir()));
}
}
RINOK(sync.ScanProgress(numFiles, totalSize, FString(), false));
// sync.SetNumFilesTotal(numFiles);
// sync.SetProgress(totalSize, 0);
// SetStatus(LangString(IDS_CHECKSUM_CALCULATING));
// sync.SetCurFilePath(L"");
SetStatus(L"");
Enumerator.Init();
FString tempPath;
FString firstFilePath;
bool isFirstFile = true;
UInt64 errorsFilesSize = 0;
for (;;)
{
bool filled;
DWORD error = Enumerator.GetNextFile(fi, filled, path);
if (error != 0)
{
AddErrorMessage(error, path);
continue;
}
if (!filled)
break;
error = 0;
Hash.InitForNewFile();
if (!fi.IsDir())
{
NIO::CInFile inFile;
tempPath = Enumerator.BasePrefix_for_Open;
tempPath += path;
if (!inFile.Open(tempPath))
{
error = GetNormalizedError();
AddErrorMessage(error, path);
continue;
}
if (isFirstFile)
{
firstFilePath = path;
isFirstFile = false;
}
sync.Set_FilePath(fs2us(path));
sync.Set_NumFilesCur(Hash.NumFiles);
UInt64 progress_Prev = 0;
for (;;)
{
UInt32 size;
if (!inFile.Read(buf, kBufSize, size))
{
error = GetNormalizedError();
AddErrorMessage(error, path);
UInt64 errorSize = 0;
if (inFile.GetLength(errorSize))
errorsFilesSize += errorSize;
break;
}
if (size == 0)
break;
Hash.Update(buf, size);
if (Hash.CurSize - progress_Prev >= ((UInt32)1 << 21))
{
RINOK(sync.Set_NumBytesCur(errorsFilesSize + Hash.FilesSize + Hash.CurSize));
progress_Prev = Hash.CurSize;
}
}
}
if (error == 0)
Hash.Final(fi.IsDir(), false, fs2us(path));
RINOK(sync.Set_NumBytesCur(errorsFilesSize + Hash.FilesSize));
}
RINOK(sync.Set_NumBytesCur(errorsFilesSize + Hash.FilesSize));
sync.Set_NumFilesCur(Hash.NumFiles);
if (Hash.NumFiles != 1)
sync.Set_FilePath(L"");
SetStatus(L"");
CProgressMessageBoxPair &pair = GetMessagePair(Hash.NumErrors != 0);
AddHashBundleRes(pair.Message, Hash, fs2us(firstFilePath));
LangString(IDS_CHECKSUM_INFORMATION, pair.Title);
return S_OK;
}
HRESULT CApp::CalculateCrc2(const UString &methodName)
{
unsigned srcPanelIndex = GetFocusedPanelIndex();
CPanel &srcPanel = Panels[srcPanelIndex];
CRecordVector<UInt32> indices;
srcPanel.GetOperatedIndicesSmart(indices);
if (indices.IsEmpty())
return S_OK;
if (!srcPanel.Is_IO_FS_Folder())
{
CCopyToOptions options;
options.streamMode = true;
options.showErrorMessages = true;
options.hashMethods.Add(methodName);
UStringVector messages;
return srcPanel.CopyTo(options, indices, &messages);
}
#ifdef EXTERNAL_CODECS
LoadGlobalCodecs();
#endif
{
CThreadCrc t;
{
UStringVector methods;
methods.Add(methodName);
RINOK(t.Hash.SetMethods(EXTERNAL_CODECS_VARS_G methods));
}
FOR_VECTOR (i, indices)
t.Enumerator.FilePaths.Add(us2fs(srcPanel.GetItemRelPath(indices[i])));
UString basePrefix = srcPanel.GetFsPath();
UString basePrefix2 = basePrefix;
if (basePrefix2.Back() == ':')
{
int pos = basePrefix2.ReverseFind_PathSepar();
if (pos >= 0)
basePrefix2.DeleteFrom(pos + 1);
}
t.Enumerator.BasePrefix = us2fs(basePrefix);
t.Enumerator.BasePrefix_for_Open = us2fs(basePrefix2);
t.Enumerator.EnterToDirs = !GetFlatMode();
t.ProgressDialog.ShowCompressionInfo = false;
UString title = LangString(IDS_CHECKSUM_CALCULATING);
t.ProgressDialog.MainWindow = _window;
t.ProgressDialog.MainTitle = L"7-Zip"; // LangString(IDS_APP_TITLE);
t.ProgressDialog.MainAddTitle = title;
t.ProgressDialog.MainAddTitle.Add_Space();
RINOK(t.Create(title, _window));
}
RefreshTitleAlways();
return S_OK;
}
void CApp::CalculateCrc(const UString &methodName)
{
HRESULT res = CalculateCrc2(methodName);
if (res != S_OK && res != E_ABORT)
{
unsigned srcPanelIndex = GetFocusedPanelIndex();
CPanel &srcPanel = Panels[srcPanelIndex];
srcPanel.MessageBoxError(res);
}
}